Merge branch 'stable-2.11' * stable-2.11: Pass edit info from ChangeScreen to PatchSetsBox Improve the wording about logical CPUs Use the same thick Cursor in inline editor Change-Id: I6f2a24e805e67a499574f0affe89b50bb46729a4 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/.buckconfig b/.buckconfig index e4a19f1..e3d0ffc 100644 --- a/.buckconfig +++ b/.buckconfig
@@ -9,6 +9,7 @@ docs = //Documentation:searchfree firefox = //:firefox gerrit = //:gerrit + headless = //:headless release = //:release safari = //:safari soyc = //gerrit-gwtui:ui_soyc
diff --git a/.buckversion b/.buckversion index 9c09744..46408a5 100644 --- a/.buckversion +++ b/.buckversion
@@ -1 +1 @@ -79d36de9f5284f6e833cca81867d6088a25685fb +8204fddf60b25a3c2090f3ef0742fca5d466d562
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1f149cf --- /dev/null +++ b/.editorconfig
@@ -0,0 +1,11 @@ +# http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2
diff --git a/.gitignore b/.gitignore index c30cee6..f2cb839 100644 --- a/.gitignore +++ b/.gitignore
@@ -21,3 +21,4 @@ /gwt-unitCache *.swp *.asc +/bin/
diff --git a/.mailmap b/.mailmap index 75aea08..c8e2f82 100644 --- a/.mailmap +++ b/.mailmap
@@ -1,21 +1,43 @@ -Adrian Görler <adrian.goerler@sap.com> Adrian Goerler <adrian.goerler@sap.com> -Alex Ryazantsev <alex.ryazantsev@gmail.com> alex <alex.ryazantsev@gmail.com> -Alex Ryazantsev <alex.ryazantsev@gmail.com> alex.ryazantsev <alex.ryazantsev@gmail.com> -Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com> -Deniz Türkoglu <deniz@spotify.com> Deniz Turkoglu <deniz@spotify.com> -Deniz Türkoglu <deniz@spotify.com> Deniz Türkoglu <deniz@spotify.com> -Edwin Kempin <edwin.kempin@sap.com> Edwin Kempin <edwin.kempin@gmail.com> -Hugo Arès <hugo.ares@ericsson.com> Hugo Ares <hugo.ares@ericsson.com> -Jason Huntley <jhuntley@houghtonassociates.com> jhuntley <jhuntley@houghtonassociates.com> -Johan Björk <jbjoerk@gmail.com> Johan Bjork <phb@spotify.com> +Adrian Görler <adrian.goerler@sap.com> Adrian Goerler <adrian.goerler@sap.com> +Ahaan Ugale <ahaanugale@gmail.com> <augale@codeaurora.org> +Alex Blewitt <alex.blewitt@gmail.com> <alex.blewitt@gs.com> +Alex Ryazantsev <alex.ryazantsev@gmail.com> alex <alex.ryazantsev@gmail.com> +Alex Ryazantsev <alex.ryazantsev@gmail.com> alex.ryazantsev <alex.ryazantsev@gmail.com> +Brad Larson <bklarson@gmail.com> <brad.larson@garmin.com> +Bruce Zu <bruce.zu@sonymobile.com> <bruce.zu@sonyericsson.com> +Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com> +David Ostrovsky <david@ostrovsky.org> <d.ostrovsky@gmx.de> +Deniz Türkoglu <deniz@spotify.com> Deniz Türkoglu <deniz@spotify.com> +Deniz Türkoglu <deniz@spotify.com> Deniz Turkoglu <deniz@spotify.com> +Edwin Kempin <edwin.kempin@sap.com> Edwin Kempin <edwin.kempin@gmail.com> +Eryk Szymanski <eryksz@gmail.com> <eryksz@google.com> +Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik@gandaraj.com> +Fredrik Luthander <fredrik.luthander@sonymobile.com> <fredrik.luthander@sonyericsson.com> +Gustaf Lundh <gustaf.lundh@sonymobile.com> <gustaf.lundh@sonyericsson.com> +Hugo Arès <hugo.ares@ericsson.com> Hugo Ares <hugo.ares@ericsson.com> +Jason Huntley <jhuntley@houghtonassociates.com> jhuntley <jhuntley@houghtonassociates.com> +Jiří Engelthaler <EngyCZ@gmail.com> <engycz@gmail.com> +Joe Onorato <onoratoj@gmail.com> <joeo@android.com> +Johan Björk <jbjoerk@gmail.com> Johan Bjork <phb@spotify.com> Lincoln Oliveira Campos Do Nascimento <lincoln.oliveiracamposdonascimento@sonyericsson.com> lincoln <lincoln.oliveiracamposdonascimento@sonyericsson.com> -Mônica Dionísio <monica.dionisio@sonyericsson.com> monica.dionisio <monica.dionisio@sonyericsson.com> -Peter Jönsson <peter.joensson@gmail.com> Peter Jönsson <peter.joensson@gmail.com> -Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com> rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com> -Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com> -Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com> -Shawn Pearce <sop@google.com> Shawn O. Pearce <sop@google.com> -Tomas Westling <thomas.westling@sonyericsson.com> thomas.westling <thomas.westling@sonyericsson.com> -Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjolin <ulrik.sjolin@gmail.com> -Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjolin <ulrik.sjolin@sonyericsson.com> -Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> +Luca Milanesio <luca.milanesio@gmail.com> <luca@gitent-scm.com> +Magnus Bäck <baeck@google.com> <magnus.back@sonyericsson.com> +Mark Derricutt <mark.derricutt@smxemail.com> <mark@talios.com> +Martin Fick <mfick@codeaurora.org> <mogulguy10@gmail.com> +Martin Fick <mfick@codeaurora.org> <mogulguy@yahoo.com> +Michael Zhou <moz@google.com> <zhoumotongxue008@gmail.com> +Mônica Dionísio <monica.dionisio@sonyericsson.com> monica.dionisio <monica.dionisio@sonyericsson.com> +Nasser Grainawi <nasser@grainawi.org> <nasser@codeaurora.org> +Nasser Grainawi <nasser@grainawi.org> <nasserg@quicinc.com> +Peter Jönsson <peter.joensson@gmail.com> Peter Jönsson <peter.joensson@gmail.com> +Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com> rafael.rabelosilva <rafael.rabelosilva@sonyericsson.com> +Richard Möhn <richard.moehn@posteo.de> <richard.moehn@fu-berlin.de> +Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com> +Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com> +Shawn Pearce <sop@google.com> Shawn O. Pearce <sop@google.com> +Tomas Westling <thomas.westling@sonyericsson.com> thomas.westling <thomas.westling@sonyericsson.com> +Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> <ulrik.sjolin@gmail.com> +Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjolin <ulrik.sjolin@gmail.com> +Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> +Ulrik Sjölin <ulrik.sjolin@sonyericsson.com> Ulrik Sjolin <ulrik.sjolin@sonyericsson.com> +Zalán Blénessy <zalanb@axis.com> Zalan Blenessy <zalanb@axis.com>
diff --git a/.settings/edu.umd.cs.findbugs.core.prefs b/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 0000000..4dfcf2d --- /dev/null +++ b/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,143 @@ +#FindBugs User Preferences +#Fri Mar 20 17:07:10 JST 2015 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCovariantArrayAssignment=CovariantArrayAssignment|false +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|false +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindComparatorProblems=FindComparatorProblems|true +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRoughConstants=FindRoughConstants|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFindUselessObjects=FindUselessObjects|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientIndexOf=InefficientIndexOf|true +detectorInefficientInitializationInsideLoop=InefficientInitializationInsideLoop|false +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableEnum=MutableEnum|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOptionalReturnNull=OptionalReturnNull|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantConditions=RedundantConditions|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|false +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=2 +effort=default +filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,MALICIOUS_CODE,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|12 +filter_settings_neg=NOISE,I18N| +run_at_full_build=false
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs index d4218a5..7397758 100644 --- a/.settings/org.eclipse.jdt.ui.prefs +++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -1,10 +1,9 @@ -#Wed Jul 29 11:31:38 PDT 2009 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Google Format formatter_settings_version=11 org.eclipse.jdt.ui.ignorelowercasenames=true -org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax; +org.eclipse.jdt.ui.importorder=\#;com.google;com;dk;eu;junit;net;org;java;javax; org.eclipse.jdt.ui.ondemandthreshold=99 org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/BUCK b/BUCK index 2cd3fa8..02ee883 100644 --- a/BUCK +++ b/BUCK
@@ -1,6 +1,7 @@ include_defs('//tools/build.defs') gerrit_war(name = 'gerrit') +gerrit_war(name = 'headless', ui = None) gerrit_war(name = 'chrome', ui = 'ui_chrome') gerrit_war(name = 'firefox', ui = 'ui_firefox') gerrit_war(name = 'safari', ui = 'ui_safari')
diff --git a/Documentation/Makefile b/Documentation/Makefile deleted file mode 100644 index aed9e90..0000000 --- a/Documentation/Makefile +++ /dev/null
@@ -1,50 +0,0 @@ -# Copyright (C) 2009 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -ASCIIDOC ?= asciidoc -ASCIIDOC_EXTRA ?= -ASCIIDOC_VER ?= 8.6.3 - -all: html - -clean: - rm -f *.html - -ASCIIDOC_EXE := $(shell which $(ASCIIDOC)) -ifeq ($(wildcard $(ASCIIDOC_EXE)),) - $(error $(ASCIIDOC) must be available) -else - ASCIIDOC_OK := $(shell expr `asciidoc --version | cut -f2 -d' '` \>= $(ASCIIDOC_VER)) - ifeq ($(ASCIIDOC_OK),0) - $(error $(ASCIIDOC) version $(ASCIIDOC_VER) or higher is required) - endif -endif - -DOC_HTML := $(patsubst %.txt,%.html,$(wildcard *.txt)) -REVISION := $(shell git describe HEAD | sed s/^v//) - -html: $(DOC_HTML) - -$(DOC_HTML): %.html : %.txt - @echo "FORMAT $@" - @rm -f $@+ $@ - @$(ASCIIDOC) -a toc \ - -a data-uri \ - -a 'revision=$(REVISION)' \ - -a 'newline=\n' \ - -b xhtml11 \ - -f asciidoc.conf \ - $(ASCIIDOC_EXTRA) \ - -o $@+ $< - @mv $@+ $@
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf deleted file mode 100644 index 2fe6213..0000000 --- a/Documentation/asciidoc.conf +++ /dev/null
@@ -1,29 +0,0 @@ -[attributes] -asterisk=* -plus=+ -caret=^ -startsb=[ -endsb=] -tilde=~ - -[specialsections] -GERRIT=gerrituplink - -[gerrituplink] -<hr style=" - height: 2px; - color: silver; - margin-top: 1.2em; - margin-bottom: 0.5em; -"> - -[macros] -(?u)^(?P<name>get)::(?P<target>\S*?)$=# - -[get-blockmacro] -<a id="{target}" onmousedown="javascript: - var i = document.URL.lastIndexOf('/Documentation/'); - var url = document.URL.substring(0, i) + '{target}'; - document.getElementById('{target}').href = url;"> - GET {target} HTTP/1.0 -</a>
diff --git a/Documentation/cmd-gc.txt b/Documentation/cmd-gc.txt index a890f1b..b7388a1 100644 --- a/Documentation/cmd-gc.txt +++ b/Documentation/cmd-gc.txt
@@ -8,6 +8,7 @@ 'ssh' -p <port> <host> 'gerrit gc' [--all] [--show-progress] + [--aggressive] <NAME> ... -- @@ -44,6 +45,9 @@ --show-progress:: If specified progress information is shown. +--aggressive:: + If an aggressive garbage collection should be done. + == EXAMPLES Run the Git garbage collection for the projects 'myProject' and
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt index 7c664ac..f3bff7d 100644 --- a/Documentation/cmd-index.txt +++ b/Documentation/cmd-index.txt
@@ -75,6 +75,9 @@ link:cmd-review.html[gerrit review]:: Verify, approve and/or submit a patch set from the command line. +link:cmd-set-head.html[gerrit set-head]:: + Change the HEAD reference of a project. + link:cmd-set-reviewers.html[gerrit set-reviewers]:: Add or remove reviewers on a change.
diff --git a/Documentation/cmd-set-head.txt b/Documentation/cmd-set-head.txt new file mode 100644 index 0000000..d74caaa --- /dev/null +++ b/Documentation/cmd-set-head.txt
@@ -0,0 +1,45 @@ += gerrit set-head + +== NAME +gerrit set-head - Change a project's HEAD. + +== SYNOPSIS +-- +'ssh' -p <port> <host> 'gerrit set-head' <NAME> + --new-head <REF> +-- + +== DESCRIPTION +Modifies a given project's HEAD reference. + +The command is argument-safe, that is, if no argument is given the +previous settings are kept intact. + +== ACCESS +Caller must be an owner of the given project. + +== SCRIPTING +This command is intended to be used in scripts. + +== OPTIONS +<NAME>:: + Required; name of the project to change the HEAD. If name ends + with `.git` the suffix will be automatically removed. + +--new-head:: + Required; name of the ref that should be set as new HEAD. The + 'refs/heads/' prefix can be omitted. + +== EXAMPLES +Change HEAD of project `example` to `stable-2.11` branch: + +==== + $ ssh -p 29418 review.example.com gerrit set-head example --new-head stable-2.11 +==== + +GERRIT +------ +Part of link:index.html[Gerrit Code Review] + +SEARCHBOX +---------
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt index c754f35..dcdbb07 100644 --- a/Documentation/cmd-stream-events.txt +++ b/Documentation/cmd-stream-events.txt
@@ -153,6 +153,19 @@ eventCreatedOn:: Time in seconds since the UNIX epoch when this event was created. +=== Project Created + +Sent when a new project has been created. + +type:: "project-created" + +projectName:: The created project name + +projectHead:: The created project head name + +eventCreatedOn:: Time in seconds since the UNIX epoch when this event was +created. + === Merge Failed Sent when a change has failed to be merged into the git repository.
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index fdb9d18..95b6717 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt
@@ -871,6 +871,30 @@ + Default is "Submit patch set ${patchSet} into ${branch}". +[[change.submitWholeTopic]]change.submitWholeTopic:: ++ +Determines if the submit button submits the whole topic instead of +just the current change. ++ +Default is false. + +[[change.submitTopicLabel]]change.submitTopicLabel:: ++ +If `change.submitWholeTopic` is set and a change has a topic, +the label name for the submit button is given here instead of +the configuration `change.submitLabel`. ++ +Defaults to "Submit whole topic" + +[[change.submitTopicTooltip]]change.submitTopicTooltip:: ++ +If `change.submitWholeTopic` is configuerd to true and a change has a +topic, this configuration determines the tooltip for the submit button +instead of `change.submitTooltip`. The variable `${topicSize}` is available +for the number of changes to be submitted. ++ +Defaults to "Submit all ${topicSize} changes within the topic". + [[change.replyLabel]]change.replyLabel:: + Label name for the reply button. In the user interface an ellipsis (…) @@ -1188,7 +1212,7 @@ [[core.useRecursiveMerge]]core.useRecursiveMerge:: + Use JGit's recursive merger for three-way merges. This only affects -projects configured to automatically resolve conflicts. +projects that allow content merges. + As explained in this link:http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html[ @@ -1428,7 +1452,7 @@ [[download.archive]]download.archive:: + Specifies which archive formats, if any, should be offered on the change -screen: +screen and supported for `git-upload-archive` operation: + ---- [download] @@ -1436,11 +1460,17 @@ archive = tbz2 archive = tgz archive = txz + archive = zip ---- If `download.archive` is not specified defaults to all archive commands. Set to `off` or empty string to disable. +Zip is not supported because it may be interpreted by a Java plugin as a +valid JAR file, whose code would have access to cookies on the domain. +For this reason `zip` format is always excluded from formats offered +through the `Download` drop down or accessible in the REST API. + [[gc]] === Section gc @@ -1448,6 +1478,15 @@ to run periodically. It will be triggered and executed sequentially for all projects. +[[gc.aggressive]]gc.aggressive:: ++ +Determines if scheduled garbage collections and garbage collections triggered +through Web-UI should run in aggressive mode or not. Aggressive garbage +collections are more expensive but may lead to significantly smaller +repositories. ++ +Valid values are "true" and "false," default is "false". + [[gc.startTime]]gc.startTime:: + Start time to define the first execution of the git garbage collection. @@ -1800,6 +1839,11 @@ Optional filename for the hashtags changed hook, if not specified then `hashtags-changed` will be used. +[[hooks.projectCreatedHook]]hooks.projectCreatedHook:: ++ +Optional filename for the project created hook, if not specified then +`project-created` will be used. + [[hooks.mergeFailedHook]]hooks.mergeFailedHook:: + Optional filename for the merge failed hook, if not specified then @@ -2156,6 +2200,36 @@ CPUs as returned by the JVM (unless link:#changeMerge.threadPoolSize[changeMerge.threadPoolSize] is set). +[[index.onlineUpgrade]]index.onlineUpgrade:: ++ +Whether to upgrade to new index schema versions while the server is +running. This is recommended as it prevents additional downtime during +Gerrit version upgrades (avoiding the need for an offline reindex step +using Reindex), but can add additional server load during the upgrade. ++ +If set to false, there is no way to upgrade the index schema to take +advantage of new search features without restarting the server. ++ +Defaults to true. + +[[index.maxLimit]]index.maxLimit:: ++ +Maximum limit to allow for search queries. Requesting results above this +limit will truncate the list (but will still set `_more_changes` on +result lists). Set to 0 for no limit. ++ +Defaults to no limit. + +[[index.maxPages]]index.maxPages:: ++ +Maximum number of pages of search results to allow, as index +implementations may have to scan through large numbers of skipped +results when searching with an offset. Requesting results starting past +this threshold times the requested limit will result in an error. Set to +0 for no limit. ++ +Defaults to no limit. + ==== Lucene configuration Open and closed changes are indexed in separate indexes named @@ -2581,6 +2655,22 @@ javaOptions = -Dcom.sun.jndi.ldap.connect.pool.timeout=300000 ---- +[[log]] +=== Section log + +[[log.jsonLogging]]log.jsonLogging:: ++ +If set to true, enables error logging in JSON format (file name: "logs/error_log.json"). ++ +Defaults to false. + +[[log.textLogging]]log.textLogging:: ++ +If set to true, enables error logging in regular plain text format. Can only be disabled +if `jsonLogging` is enabled. ++ +Defaults to true. + [[mimetype]] === Section mimetype @@ -2657,6 +2747,13 @@ and SSH. If set to true Administrators can install new plugins remotely, or disable existing plugins. Defaults to false. +[[plugins.jsLoadTimeout]]plugins.jsLoadTimeout:: ++ +Set the timeout value for loading JavaScript plugins in Gerrit UI. +Values can be specified using standard time unit abbreviations ('ms', +'sec', 'min', etc.). ++ +Default is 5 seconds. Negative values will be converted to 0. [[receive]] === Section receive @@ -2788,9 +2885,22 @@ ownerGroup = Registered Users ---- -[NOTE] -Currently only the repository name `*` is supported. -This is a wildcard designating all repositories. +The only matching patterns supported are exact match or wildcard matching which +can be specified by ending the name with a `*`. If a project matches more than one +repository configuration, then the configuration from the more precise match +will be used. In the following example, the default submit type for a project +named `project/plugins/a` would be `CHERRY_PICK`. + +---- +[repository "project/*"] + defaultSubmitType = MERGE_IF_NECESSARY +[repository "project/plugins/*"] + defaultSubmitType = CHERRY_PICK +---- + +[NOTE] All properties are used from the matching repository configuration. In +the previous example, all properties will be used from `project/plugins/\*` +section and no properties will be inherited nor overridden from `project/*`. [[repository.name.defaultSubmitType]]repository.<name>.defaultSubmitType:: +
diff --git a/Documentation/config-hooks.txt b/Documentation/config-hooks.txt index ce908b9..3068cc5 100644 --- a/Documentation/config-hooks.txt +++ b/Documentation/config-hooks.txt
@@ -110,6 +110,14 @@ ref-updated --oldrev <old rev> --newrev <new rev> --refname <ref name> --project <project name> --submitter <submitter> ==== +=== project-created + +Called whenever a project has been created. + +==== + project-created --project <project name> --head <head name> +==== + === reviewer-added Called whenever a reviewer is added to a change.
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt index ce49d31..a40c5f3 100644 --- a/Documentation/config-labels.txt +++ b/Documentation/config-labels.txt
@@ -243,7 +243,7 @@ This is the case if the change was rebased onto a different parent. This can be used to enable sticky approvals, reducing turn-around for trivial rebases prior to submitting a change. -It is recommended to enable this for the Code-Review label. +For the pre-installed Code-Review label this is enabled by default. Defaults to false. [[label_copyAllScoresIfNoCodeChange]] @@ -255,7 +255,8 @@ the commit message is different. This can be used to enable sticky approvals on labels that only depend on the code, reducing turn-around if only the commit message is changed prior to submitting a change. -It is recommended to enable this for the Verified label if enabled. +For the Verified label that is installed by the link:pgm-init.html[init] +site program this is enabled by default. Defaults to false. [[label_copyAllScoresIfNoChange]]
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt index 608a3ac..3887ff3 100644 --- a/Documentation/config-plugins.txt +++ b/Documentation/config-plugins.txt
@@ -386,6 +386,17 @@ link:https://gerrit.googlesource.com/plugins/motd/+doc/master/src/main/resources/Documentation/config.md[ Configuration] +[[oauth-authentication-provider]] +=== OAuth authentication provider +This plugin enables Gerrit to use OAuth2 protocol for authentication. +Two different OAuth providers are supported: + +* GitHub +* Google + +https://github.com/davido/gerrit-oauth-provider[Project] | +https://github.com/davido/gerrit-oauth-provider/wiki/Getting-Started[Configuration] + [[project-download-commands]] === project-download-commands
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt index 7f30b14..c3dd12e 100644 --- a/Documentation/config-reverseproxy.txt +++ b/Documentation/config-reverseproxy.txt
@@ -48,6 +48,8 @@ <Proxy *> Order deny,allow Allow from all + # Use following line instead of the previous two on Apache >= 2.4 + # Require all granted </Proxy> AllowEncodedSlashes On
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt index 2eb478c..89cd849 100644 --- a/Documentation/dev-buck.txt +++ b/Documentation/dev-buck.txt
@@ -7,7 +7,7 @@ There is currently no binary distribution of Buck, so it has to be manually built and installed. Apache Ant is required. Currently only Linux and Mac -OS are supported. Buck requires Python version 2.7 to be installed. +OS are supported. Clone the git and build it: @@ -50,6 +50,10 @@ script from `./scripts/buck_completion.bash` in the buck project. Refer to the script's header comments for installation instructions. +== Prerequisites + +Buck requires Python version 2.7 to be installed. The Maven download toolchain +requires `curl` to be installed. [[eclipse]] == Eclipse Integration @@ -116,6 +120,20 @@ ---- +=== Headless Mode + +To build Gerrit in headless mode, i.e. without the GWT Web UI: + +---- + buck build headless +---- + +The output executable WAR will be placed in: + +---- + buck-out/gen/headless.war +---- + === Extension and Plugin API JAR Files To build the extension, plugin and GWT API JAR files: @@ -593,14 +611,14 @@ needs to be repeated, the unit test cache for that test must be removed first: ---- - rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/.AddRemoveGroupMembersIT/ + rm -rf buck-out/bin/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/.AddRemoveGroupMembersIT/ ---- After clearing the cache, the test can be run again: ---- - buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT - TESTING //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group:AddRemoveGroupMembersIT + buck test //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group:AddRemoveGroupMembersIT + TESTING //gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group:AddRemoveGroupMembersIT PASS 14,9s 8 Passed 0 Failed com.google.gerrit.acceptance.rest.group.AddRemoveGroupMembersIT TESTS PASSED ----
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt index 72d7ddf..b717f4e 100644 --- a/Documentation/dev-contributing.txt +++ b/Documentation/dev-contributing.txt
@@ -334,6 +334,8 @@ plugins that have a dependency on GWT. * Update the GWT version in the archetype metadata in the `gerrit-plugin-gwt-archetype`. +* Update the version of `gwt-maven-plugin` in the example pom.xml file in +link:dev-plugins.html[dev-plugins]. * Update to the same GWT version in the `gwtjsonrpc` project, and release a new version.
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index f9b66cb..9bb0007 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt
@@ -36,7 +36,7 @@ ---- mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \ -DarchetypeArtifactId=gerrit-plugin-archetype \ - -DarchetypeVersion=2.10 \ + -DarchetypeVersion=2.12-SNAPSHOT \ -DgroupId=com.googlesource.gerrit.plugins.testplugin \ -DartifactId=testplugin ---- @@ -404,6 +404,10 @@ + Publication of usage data +* `com.google.gerrit.extensions.events.GarbageCollectorListener`: ++ +Garbage collection ran on a project + [[stream-events]] == Sending Events to the Events Stream @@ -1519,7 +1523,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> - <version>2.5.1</version> + <version>2.7.0</version> <configuration> <module>com.googlesource.gerrit.plugins.myplugin.HelloPlugin</module> <disableClassMetadata>true</disableClassMetadata> @@ -1717,17 +1721,18 @@ [[data-directory]] == Data Directory -Plugins can request a data directory with a `@PluginData` File -dependency. A data directory will be created automatically by the -server in `$site_path/data/$plugin_name` and passed to the plugin. +Plugins can request a data directory with a `@PluginData` Path (or File, +deprecated) dependency. A data directory will be created automatically +by the server in `$site_path/data/$plugin_name` and passed to the +plugin. Plugins can use this to store any data they want. [source,java] ---- @Inject -MyType(@PluginData java.io.File myDir) { - new FileInputStream(new File(myDir, "my.config")); +MyType(@PluginData java.nio.file.Path myDir) { + this.in = Files.newInputStream(myDir.resolve("my.config")); } ----
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt index bcf4a588..b64973a 100644 --- a/Documentation/dev-readme.txt +++ b/Documentation/dev-readme.txt
@@ -56,6 +56,17 @@ Eclipse integration and then open it as Eclipse project in IDEA. You need the Eclipse plugin activated in IntelliJ IDEA. +Once you start compiling using both buck and your Gerrit project in +IDEA, you will likely need to mark the below directories as generated +sources roots. You can do so using the IDEA "Project" view. In the +context menu of each one of these, use "Mark Directory As" to mark +them as "Generated Sources Root": + +---- + __auto_value_tests_gen__ + __httpd_gen__ + __server_gen__ +---- == Mac OS X
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt index f3397a4..b2c5358 100644 --- a/Documentation/dev-release-deploy-config.txt +++ b/Documentation/dev-release-deploy-config.txt
@@ -10,12 +10,11 @@ To be able to publish artifacts to Maven Central some preparations must be done: -* Create a Sonatype account as described in the -link:https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-2.Signup[ -Sonatype OSS Maven Repository Usage Guide]. +* Create an account on +link:https://issues.sonatype.org/secure/Signup!default.jspa[Sonatype's Jira]. + Sonatype is the company that runs Maven Central and you need a Sonatype -account for uploading artifacts to Maven Central. +account to be able to upload artifacts to Maven Central. * Configure your Sonatype user and password in `~/.m2/settings.xml`: + @@ -75,7 +74,7 @@ Gerrit Subproject Artifacts are stored on link:https://developers.google.com/storage/[Google Cloud Storage]. -Via the link:https://code.google.com/apis/console/?noredirect[API Console] the +Via the link:https://console.developers.google.com/project/164060093628[Developers Console] the Gerrit maintainers have access to the `Gerrit Code Review` project. This projects host several buckets for storing Gerrit artifacts: @@ -90,13 +89,14 @@ To upload artifacts to a bucket the user must authenticate with a username and password. The username and password need to be retrieved -from the link:https://code.google.com/apis/console/?noredirect[API Console]: +from the link:https://console.developers.google.com/project/164060093628[ +Google Developers Console]: -* Go to the `Gerrit Code Review` project -* In the menu on the left select `Google Cloud Storage` > -`Interoperable Access` -* Use the `Access Key` as username -* Click under `Secret` on the `Show` button to find the password +* In the menu on the left select `Storage` -> `Cloud Storage` > +> `Storage access` +* Select the `Interoperability` tab +* If no keys are listed under `Interoperable storage access keys`, select "Create a new key" +* Use the `Access Key` as username, and `Secret` as the password To make the username and password known to Maven, they must be configured in the `~/.m2/settings.xml` file.
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt index 3d3104c..1b3e98f 100644 --- a/Documentation/dev-release.txt +++ b/Documentation/dev-release.txt
@@ -157,7 +157,8 @@ * Build the Gerrit WAR and API JARs + ---- - buck build release + buck clean + buck build --no-cache release buck build api_install ---- @@ -352,9 +353,7 @@ * Upload the html files manually via web browser to the link:https://console.developers.google.com/project/164060093628/storage/gerrit-documentation/[ -gerrit-documentation] storage bucket. The `gerrit-documentation` -storage bucket is accessible via the -link:https://cloud.google.com/console[Google Developers Console]. +gerrit-documentation] storage bucket. [[update-links]] ==== Update Google Code project links
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt index f5d5277..eb3c4fb 100644 --- a/Documentation/intro-project-owner.txt +++ b/Documentation/intro-project-owner.txt
@@ -238,7 +238,7 @@ === Submit Type An important decision for a project is the choice of the submit type -and the content merge setting (aka `Automatically resolve conflicts`). +and the content merge setting (see the `Allow content merges` option). The link:project-configuration.html#submit_type[submit type] is the method Gerrit uses to submit a change to the project. The submit type defines what Gerrit should do on submit of a change if the destination branch
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt index 4677307..4584841 100644 --- a/Documentation/intro-user.txt +++ b/Documentation/intro-user.txt
@@ -29,6 +29,9 @@ * link:http://eclipse.org/mylyn/[Mylyn Gerrit Connector]: Gerrit integration with Mylyn +* link:https://github.com/uwolfer/gerrit-intellij-plugin[Gerrit + IntelliJ Plugin]: Gerrit integration with the + link:http://www.jetbrains.com/idea/[IntelliJ Platform] * link:https://play.google.com/store/apps/details?id=com.jbirdvegas.mgerrit[ mGerrit]: Android client for Gerrit * link:https://github.com/stackforge/gertty[Gertty]: Console-based
diff --git a/Documentation/json.txt b/Documentation/json.txt index feef1a1..8ccd03b 100644 --- a/Documentation/json.txt +++ b/Documentation/json.txt
@@ -156,7 +156,7 @@ newRev:: The new value the ref was updated to. -refName:: Ref name within project. +refName:: Full ref name within project. project:: Project path in Gerrit.
diff --git a/Documentation/man/.gitignore b/Documentation/man/.gitignore deleted file mode 100644 index 7993a55..0000000 --- a/Documentation/man/.gitignore +++ /dev/null
@@ -1 +0,0 @@ -gerrit*
diff --git a/Documentation/man/Makefile b/Documentation/man/Makefile deleted file mode 100644 index 945f215..0000000 --- a/Documentation/man/Makefile +++ /dev/null
@@ -1,64 +0,0 @@ -# Copyright (C) 2013 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -A2X ?= a2x - -all: man - -clean: - @rm -f gerrit-* - -CMD_CORE_SSH_CMD := \ -cmd-ban-commit.txt \ -cmd-create-account.txt \ -cmd-create-group.txt \ -cmd-create-project.txt \ -cmd-flush-caches.txt \ -cmd-gc.txt \ -cmd-gsql.txt \ -cmd-ls-groups.txt \ -cmd-ls-projects.txt \ -cmd-ls-user-refs.txt \ -cmd-query.txt \ -cmd-rename-group.txt \ -cmd-review.txt \ -cmd-set-account.txt \ -cmd-set-members.txt \ -cmd-set-project-parent.txt \ -cmd-set-project.txt \ -cmd-set-reviewers.txt \ -cmd-show-caches.txt \ -cmd-show-connections.txt \ -cmd-show-queue.txt \ -cmd-stream-events.txt \ -cmd-test-submit-rule.txt \ -cmd-version.txt - -GERRIT_CORE_SSH_CMD := $(patsubst cmd-%,gerrit-%,$(CMD_CORE_SSH_CMD)) -DOC_MAN := $(patsubst %.txt,%.1,$(GERRIT_CORE_SSH_CMD)) - -man: $(GERRIT_CORE_SSH_CMD) $(DOC_MAN) - -$(GERRIT_CORE_SSH_CMD) : gerrit-%.txt : ../cmd-%.txt - @cp $< $@ - -$(DOC_MAN) : %.1 : %.txt - @echo "creating man page for $@ ..." - @rm -f $@ - @$(eval TITLE := $(join $(basename $<),\(1\))) - @$(eval SEPERATOR := $(shell echo $(TITLE) | sed 's/./=/g')) - @sed -i -re '1s/^.*$//$(TITLE)/' $< - @sed -i -re '2s/^=.*/$(SEPERATOR)/' $< - @sed -i -re '6s/^gerrit\s+(\w)/gerrit-\1/' $< - @$(A2X) --doctype manpage --format manpage $<
diff --git a/Documentation/project-configuration.txt b/Documentation/project-configuration.txt index 1cecabf..6d7c6d0 100644 --- a/Documentation/project-configuration.txt +++ b/Documentation/project-configuration.txt
@@ -114,7 +114,7 @@ the same file has also been changed on the other side of the merge. [[content_merge]] -If `Automatically resolve conflicts` is enabled, Gerrit will try +If `Allow content merges` is enabled, Gerrit will try to do a content merge when a path conflict occurs. [[project-state]]
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 1b9a705..138f85f 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt
@@ -310,6 +310,13 @@ * `CHECK`: include potential problems with the change. -- +[[commit-footers]] +-- +* `COMMIT_FOOTERS`: include the full commit message with + Gerrit-specific commit footers in the + link:#revision-info[RevisionInfo]. +-- + .Request ---- GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES&o=DOWNLOAD_COMMANDS HTTP/1.0 @@ -1146,6 +1153,109 @@ HTTP/1.1 204 No Content ---- +[[list-change-comments]] +=== List Change Comments +-- +'GET /changes/link:#change-id[\{change-id\}]/comments' +-- + +Lists the published comments of all revisions of the change. + +Returns a map of file paths to lists of link:#comment-info[CommentInfo] +entries. The entries in the map are sorted by file path, and the +comments for each path are sorted by patch set number. Each comment has +the `patch_set` and `author` fields set. + +.Request +---- + GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/comments HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": [ + { + "patch_set": 1, + "id": "TvcXrmjM", + "line": 23, + "message": "[nit] trailing whitespace", + "updated": "2013-02-26 15:40:43.986000000" + "author": { + "_account_id": 1000096, + "name": "John Doe", + "email": "john.doe@example.com" + } + }, + { + "patch_set": 2, + "id": "TveXwFiA", + "line": 49, + "in_reply_to": "TfYX-Iuo", + "message": "Done", + "updated": "2013-02-26 15:40:45.328000000" + "author": { + "_account_id": 1000097, + "name": "Jane Roe", + "email": "jane.roe@example.com" + } + } + ] + } +---- + +[[list-change-drafts]] +=== List Change Drafts +-- +'GET /changes/link:#change-id[\{change-id\}]/drafts' +-- + +Lists the draft comments of all revisions of the change that belong to +the calling user. + +Returns a map of file paths to lists of link:#comment-info[CommentInfo] +entries. The entries in the map are sorted by file path, and the +comments for each path are sorted by patch set number. Each comment has +the `patch_set` field set, and no `author`. + +.Request +---- + GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/drafts HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": [ + { + "patch_set": 1, + "id": "TvcXrmjM", + "line": 23, + "message": "[nit] trailing whitespace", + "updated": "2013-02-26 15:40:43.986000000" + }, + { + "patch_set": 2, + "id": "TveXwFiA", + "line": 49, + "in_reply_to": "TfYX-Iuo", + "message": "Done", + "updated": "2013-02-26 15:40:45.328000000" + } + ] + } +---- + [[check-change]] === Check change -- @@ -2586,7 +2696,7 @@ ---- [[list-drafts]] -=== List Drafts +=== List Revision Drafts -- 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/drafts/' -- @@ -2594,9 +2704,8 @@ Lists the draft comments of a revision that belong to the calling user. -As result a map is returned that maps the file path to a list of -link:#comment-info[CommentInfo] entries. The entries in the map are -sorted by file path. +Returns a map of file paths to lists of link:#comment-info[CommentInfo] +entries. The entries in the map are sorted by file path. .Request ---- @@ -2765,7 +2874,7 @@ ---- [[list-comments]] -=== List Comments +=== List Revision Comments -- 'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/comments/' -- @@ -3494,6 +3603,9 @@ [options="header",cols="1,^1,5"] |=========================== |Field Name ||Description +|`patch_set` |optional| +The patch set number for the comment; only set in contexts where + +comments may be returned for multiple patch sets. |`id` ||The URL encoded UUID of the comment. |`path` |optional| The path of the file for which the inline comment was done. + @@ -4013,7 +4125,9 @@ Draft handling that defines how draft comments are handled that are already in the database but that were not also described in this input. + -Allowed values are `DELETE`, `PUBLISH` and `KEEP`. + +Allowed values are `DELETE`, `PUBLISH`, `PUBLISH_ALL_REVISIONS` and +`KEEP`. All values except `PUBLISH_ALL_REVISIONS` operate only on drafts +for a single revision. + If not set, the default is `DELETE`. |`notify` |optional| Notify handling that defines to whom email notifications should be sent @@ -4108,6 +4222,12 @@ |`reviewed` |optional| Indicates whether the caller is authenticated and has commented on the current revision. Only set if link:#reviewed[REVIEWED] option is requested. +|`messageWithFooter` |optional| +If the link:#commit-footers[COMMIT_FOOTERS] option is requested and +this is the current patch set, contains the full commit message with +Gerrit-specific commit footers, as if this revision were submitted +using the link:project-configuration.html#cherry_pick[Cherry Pick] +submit type. |=========================== [[rule-input]]
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt index 0ee6966..a8bbbfa 100644 --- a/Documentation/rest-api-config.txt +++ b/Documentation/rest-api-config.txt
@@ -30,6 +30,84 @@ "2.7" ---- +[[get-info]] +=== Get Server Info +-- +'GET /config/server/info' +-- + +Returns the information about the Gerrit server configuration. + +.Request +---- + GET /config/server/info HTTP/1.0 +---- + +As result a link:#server-info[ServerInfo] entity is returned. + +.Response +---- + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "auth": { + "auth_type": "LDAP", + "editable_account_fields": [ + "FULL_NAME", + "REGISTER_NEW_EMAIL" + ] + }, + "download": { + "schemes": [ + "anonymous http": { + "url": "http://gerrithost:8080/${project}", + "commands": { + "Checkout": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD", + "Format Patch": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull http://gerrithost:8080/${project} ${ref}", + "Cherry Pick": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD" + } + }, + "http": { + "url": "http://jdoe@gerrithost:8080/${project}", + "is_auth_required": true, + "is_auth_supported": true, + "commands": { + "Checkout": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD", + "Format Patch": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull http://jdoe@gerrithost:8080/${project} ${ref}", + "Cherry Pick": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD" + } + }, + "ssh": { + "url": "ssh://jdoe@gerrithost:29418/${project}", + "is_auth_required": true, + "is_auth_supported": true, + "commands": { + "Checkout": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD", + "Format Patch": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD", + "Pull": "git pull ssh://jdoe@gerrithost:29418/${project} ${ref}", + "Cherry Pick": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD" + } + } + ], + "archives": [ + "tgz", + "tar", + "tbz2", + "txz" + ] + }, + "gerrit": { + "all_projects": "All-Projects", + "all_users": "All-Users" + } + } +---- + + [[list-caches]] === List Caches -- @@ -822,6 +900,27 @@ [[json-entities]] == JSON Entities +[[auth-info]] +=== AuthInfo +The `AuthInfo` entity contains information about the authentication +configuration of the Gerrit server. + +[options="header",cols="1,^1,5"] +|========================================== +|Field Name ||Description +|`type` || +The link:config-gerrit.html#auth.type[authentication type] that is +configured on the server. Can be `OPENID`, `OPENID_SSO`, `OAUTH`, +`HTTP`, `HTTP_LDAP`, `CLIENT_SSL_CERT_LDAP`, `LDAP`, `LDAP_BIND`, +`CUSTOM_EXTENSION` or `DEVELOPMENT_BECOME_ANY_ACCOUNT`. +|`use_contributor_agreements` |not set if `false`| +Whether link:config-gerrit.html#auth.contributorAgreements[contributor +agreements] are required. +|`editable_account_fields` || +List of account fields that are editable. Possible values are +`FULL_NAME`, `USER_NAME` and `REGISTER_NEW_EMAIL`. +|========================================== + [[cache-info]] === CacheInfo The `CacheInfo` entity contains information about a cache. @@ -878,6 +977,59 @@ |`name` |capability name |================================= +[[contact-store-info]] +=== ContactStoreInfo +The `ContactStoreInfo` entity contains information about the contact +store. + +[options="header",cols="1,6"] +|======================= +|Field Name |Description +|`url` | +The link:config-gerrit.html#contactstore.url[URL of the contact store]. +|======================= + +[[download-info]] +=== DownloadInfo +The `DownloadInfo` entity contains information about supported download +options. + +[options="header",cols="1,6"] +|======================= +|Field Name |Description +|`schemes` | +The supported download schemes as a map which maps the scheme name to a +of link:#download-scheme-info[DownloadSchemeInfo] entity. +|`archives` | +List of supported archive formats. Possible values are `tgz`, `tar`, +`tbz2` and `txz`. +|======================= + +[[download-scheme-info]] +=== DownloadSchemeInfo +The `DownloadSchemeInfo` entity contains information about a supported +download scheme and its commands. + +[options="header",cols="1,^1,5"] +|================================= +|Field Name ||Description +|`url` || +The URL of the download scheme, where '${project}' is used as +placeholder for the project name. +|`is_auth_required` |not set if `false`| +Whether this download scheme requires authentication. +|`is_auth_supported` |not set if `false`| +Whether this download scheme supports authentication. +|`commands` || +Download commands as a map which maps the command name to the download +command. In the download command '${project}' is used as +placeholder for the project name, and '${ref}' is used as +placeholder for the (change) ref. + +Empty, if accessed anonymously and the download scheme requires +authentication. +|================================= + [[entries-info]] === EntriesInfo The `EntriesInfo` entity contains information about the entries in a @@ -896,6 +1048,21 @@ `g`: gigabytes). Only set for disk caches. |================================== +[[gerrit-info]] +=== GerritInfo +The `GerritInfo` entity contains information about Gerrit +configuration from the link:config-gerrit.html#gerrit[gerrit] section. + +[options="header",cols="1,6"] +|================================ +|Field Name |Description +|`all_projects_name` | +Name of the link:config-gerrit.html#gerrit.allProjects[root project]. +|`all_users_name` | +Name of the link:config-gerrit.html#gerrit.allUsers[project in which +meta data of all users is stored]. +|================================ + [[hit-ration-info]] === HitRatioInfo The `HitRatioInfo` entity contains information about the hit ratio of a @@ -958,6 +1125,30 @@ The number of open files. |============================ +[[server-info]] +=== ServerInfo +The `ServerInfo` entity contains information about the configuration of +the Gerrit server. + +[options="header",cols="1,^1,5"] +|======================================= +|Field Name ||Description +|`auth` || +Information about the authentication configuration as +link:#auth-info[AuthInfo] entity. +|`contact_store` |optional| +Information about the contact store configuration as +link:#contact-store-info[ContactStoreInfo] entity. +|`download` || +Information about the configured download options as +link:#download-info[DownloadInfo] entity. +information about Gerrit +|`gerrit` || +Information about the configuration from the +link:config-gerrit.html#gerrit[gerrit] section as link:#gerrit-info[ +GerritInfo] entity. +|======================================= + [[summary-info]] === SummaryInfo The `SummaryInfo` entity contains information about the current state
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index a909ab4..a25c7bb 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt
@@ -2116,6 +2116,8 @@ |Field Name ||Description |`show_progress` |`false` if not set| Whether progress information should be shown. +|`aggressive` |`false` if not set| +Whether an aggressive garbage collection should be done. |============================= [[head-input]]
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt index 5ad6b39f..a89d0db 100644 --- a/Documentation/user-inline-edit.txt +++ b/Documentation/user-inline-edit.txt
@@ -44,10 +44,9 @@ While in edit mode, it is possible to add new files to the change by clicking the 'Add...' button at the top of the file list. -Files can be removed from the change, or restored, by clicking the icon to the -left of the file name. Reverting a file in the change is also supported and is -achieved in two steps: remove file from the change and restore the file in the -change. +File changes can be reverted or files can be removed from the change or +deleted files can be restored, by clicking the icons to the left of the file +name. To switch from edit mode back to review mode, click the 'Done Editing' button.
diff --git a/Documentation/user-named-queries.txt b/Documentation/user-named-queries.txt new file mode 100644 index 0000000..0ddbbef --- /dev/null +++ b/Documentation/user-named-queries.txt
@@ -0,0 +1,32 @@ += Gerrit Code Review - Named Queries + +[[user-named-queries]] +== User Named Queries +It is possible to define named queries on a user level. To do +this, define the named queries in the `queries` file of +the user's account ref in the `All-Users` project. The user's +account ref is based on the user's account id which is an +integer. The account refs are sharded by the last two digits +(`+nn+`) in the refname, leading to refs of the format +`+refs/users/nn/accountid+`. The user's queries file is a +2 column tab delimited file. The left column represents the +name of the query, and the right column represents the query +expression represented by the name. + +Example queries file: + +---- +# Name Query +# +selfapproved owner:self label:code-review+2,user=self +blocked label:code-review-2 OR label:verified-1 +# Note below how to reference your own named queries in other named queries +ready label:code-review+2 label:verified+1 -query:blocked status:open +---- + +GERRIT +------ +Part of link:index.html[Gerrit Code Review] + +SEARCHBOX +---------
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt index 3de45d2..ca29e44 100644 --- a/Documentation/user-search.txt +++ b/Documentation/user-search.txt
@@ -99,6 +99,12 @@ + Changes originally submitted by a user in 'GROUP'. +[[query]] +query:'NAME':: ++ +Changes which match the current user's query named 'NAME' +(see link:user-named-queries.html[Named Queries]). + [[reviewer]] reviewer:'USER', r:'USER':: + @@ -327,6 +333,19 @@ Valid relations are >=, >, <=, <, or no relation, which will match if the number of lines is exactly equal. +[[commentby]] +commentby:'USER':: ++ +Changes containing a top-level or inline comment by 'USER'. The special +case of `commentby:self` will find changes where the caller has +commented. + +[[from]] +from:'USER':: ++ +Changes containing a top-level or inline comment by 'USER', or owned by +'USER'. Equivalent to `(owner:USER OR commentby:USER)`. + == Argument Quoting
diff --git a/ReleaseNotes/ReleaseNotes-2.12.txt b/ReleaseNotes/ReleaseNotes-2.12.txt new file mode 100644 index 0000000..dfdcd37 --- /dev/null +++ b/ReleaseNotes/ReleaseNotes-2.12.txt
@@ -0,0 +1,41 @@ +Release notes for Gerrit 2.12 +============================= + + +Gerrit 2.12 is now available: + +link:https://gerrit-releases.storage.googleapis.com/gerrit-2.12.war[ +https://gerrit-releases.storage.googleapis.com/gerrit-2.12.war] + +Important Notes +--------------- + + +*WARNING:* Upgrading to 2.12.x requires the server be first upgraded to 2.8 (or +2.9) and then to 2.12.x. If you are upgrading from 2.8.x or later, you may ignore +this warning and upgrade directly to 2.12.x. + +*WARNING:* When upgrading from version 2.8.4 or older with a site that uses +Bouncy Castle Crypto, new versions of the libraries will be downloaded. The old +libraries should be manually removed from site's `lib` folder to prevent the +startup failure described in +link:https://code.google.com/p/gerrit/issues/detail?id=3084[Issue 3084]. + + +Release Highlights +------------------ + +* TODO + + +New Features +------------ + +* TODO + + +Upgrades +-------- + +* Upgrade gson to 2.3.1 +
diff --git a/VERSION b/VERSION index 9791ceb..83d3d2a 100644 --- a/VERSION +++ b/VERSION
@@ -2,4 +2,4 @@ # Used by :api_install and :api_deploy targets # when talking to the destination repository. # -GERRIT_VERSION = '2.11.1' +GERRIT_VERSION = '2.12-SNAPSHOT'
diff --git a/gerrit-acceptance-tests/BUCK b/gerrit-acceptance-tests/BUCK index c7bea4e..b6a7b29 100644 --- a/gerrit-acceptance-tests/BUCK +++ b/gerrit-acceptance-tests/BUCK
@@ -28,12 +28,14 @@ '//lib:servlet-api-3_1', '//lib:truth', + '//lib/auto:auto-value', '//lib/httpcomponents:httpclient', '//lib/httpcomponents:httpcore', '//lib/log:impl_log4j', '//lib/log:log4j', '//lib/guice:guice', '//lib/guice:guice-assistedinject', + '//lib/guice:guice-servlet', '//lib/jgit:jgit', '//lib/jgit:junit', '//lib/mina:sshd',
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java index ff61899..0b40891 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -14,32 +14,36 @@ package com.google.gerrit.acceptance; -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; -import static com.google.gerrit.acceptance.GitUtil.createProject; import static com.google.gerrit.acceptance.GitUtil.initSsh; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.server.project.Util.block; -import com.google.common.base.Joiner; +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.Sets; import com.google.common.primitives.Chars; import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.extensions.api.GerritApi; +import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.api.changes.RevisionApi; +import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.client.ListChangesOption; +import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.config.AllProjectsName; +import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.MetaDataUpdate; @@ -56,12 +60,16 @@ import com.google.inject.Provider; import com.google.inject.util.Providers; -import org.apache.http.HttpStatus; import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.Transport; +import org.junit.AfterClass; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -69,16 +77,23 @@ import org.junit.runners.model.Statement; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.EnumSet; import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; @RunWith(ConfigSuite.class) public abstract class AbstractDaemonTest { + private static GerritServer commonServer; + @ConfigSuite.Parameter public Config baseConfig; + @ConfigSuite.Name + private String configName; + @Inject protected AllProjectsName allProjects; @@ -92,7 +107,7 @@ protected GerritApi gApi; @Inject - private AcceptanceTestRequestScope atrScope; + protected AcceptanceTestRequestScope atrScope; @Inject private IdentifiedUser.GenericFactory identifiedUserFactory; @@ -119,9 +134,20 @@ protected Provider<InternalChangeQuery> queryProvider; @Inject - protected @GerritServerConfig Config cfg; + @CanonicalWebUrl + protected Provider<String> canonicalWebUrl; - protected Git git; + @Inject + @GerritServerConfig + protected Config cfg; + + @Inject + private InProcessProtocol inProcessProtocol; + + @Inject + private Provider<AnonymousUser> anonymousUser; + + protected TestRepository<InMemoryRepository> testRepo; protected GerritServer server; protected TestAccount admin; protected TestAccount user; @@ -131,6 +157,9 @@ protected ReviewDb db; protected Project.NameKey project; + private String resourcePrefix; + private List<Repository> toClose; + @Rule public TestRule testRunner = new TestRule() { @Override @@ -138,30 +167,24 @@ return new Statement() { @Override public void evaluate() throws Throwable { - boolean mem = description.getAnnotation(UseLocalDisk.class) == null; - boolean enableHttpd = description.getAnnotation(NoHttpd.class) == null - && description.getTestClass().getAnnotation(NoHttpd.class) == null; - beforeTest(config(description), mem, enableHttpd); - base.evaluate(); - afterTest(); + beforeTest(description); + try { + base.evaluate(); + } finally { + afterTest(); + } } }; } }; - private Config config(Description description) { - GerritConfigs cfgs = description.getAnnotation(GerritConfigs.class); - GerritConfig cfg = description.getAnnotation(GerritConfig.class); - if (cfgs != null && cfg != null) { - throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig not both"); + @AfterClass + public static void stopCommonServer() throws Exception { + if (commonServer != null) { + commonServer.stop(); + commonServer = null; } - if (cfgs != null) { - return ConfigAnnotationParser.parse(baseConfig, cfgs); - } else if (cfg != null) { - return ConfigAnnotationParser.parse(baseConfig, cfg); - } else { - return baseConfig; - } + TempFileUtil.cleanup(); } protected static Config submitWholeTopicEnabledConfig() { @@ -184,9 +207,24 @@ return cfg.getBoolean("change", null, "submitWholeTopic", false); } - private void beforeTest(Config cfg, boolean memory, boolean enableHttpd) throws Exception { - server = startServer(cfg, memory, enableHttpd); + private void beforeTest(Description description) throws Exception { + GerritServer.Description classDesc = + GerritServer.Description.forTestClass(description, configName); + GerritServer.Description methodDesc = + GerritServer.Description.forTestMethod(description, configName); + + if (classDesc.equals(methodDesc)) { + if (commonServer == null) { + commonServer = GerritServer.start(classDesc, baseConfig); + } + server = commonServer; + } else { + server = GerritServer.start(methodDesc, baseConfig); + } + server.getTestInjector().injectMembers(this); + Transport.register(inProcessProtocol); + toClose = Collections.synchronizedList(new ArrayList<Repository>()); admin = accounts.admin(); user = accounts.user(); adminSession = new RestSession(server, admin); @@ -196,56 +234,167 @@ Context ctx = newRequestContext(admin); atrScope.set(ctx); sshSession = ctx.getSession(); - project = new Project.NameKey("p"); - createProject(sshSession, project.get()); - git = cloneProject(sshSession.getUrl() + "/" + project.get()); + sshSession.open(); + resourcePrefix = UNSAFE_PROJECT_NAME.matcher( + description.getClassName() + "_" + + description.getMethodName() + "_").replaceAll(""); + + project = createProject(projectInput(description)); + testRepo = cloneProject(project, getCloneAsAccount(description)); } - protected GerritServer startServer(Config cfg, boolean memory, - boolean enableHttpd) throws Exception { - return GerritServer.start(cfg, memory, enableHttpd); + private TestAccount getCloneAsAccount(Description description) { + TestProjectInput ann = description.getAnnotation(TestProjectInput.class); + return accounts.get(ann != null ? ann.cloneAs() : "admin"); + } + + private ProjectInput projectInput(Description description) { + ProjectInput in = new ProjectInput(); + TestProjectInput ann = description.getAnnotation(TestProjectInput.class); + in.name = name("project"); + if (ann != null) { + in.parent = Strings.emptyToNull(ann.parent()); + in.description = Strings.emptyToNull(ann.description()); + in.createEmptyCommit = ann.createEmptyCommit(); + in.submitType = ann.submitType(); + in.useContentMerge = ann.useContributorAgreements(); + in.useSignedOffBy = ann.useSignedOffBy(); + in.useContentMerge = ann.useContentMerge(); + } else { + // Defaults should match TestProjectConfig, omitting nullable values. + in.createEmptyCommit = true; + } + updateProjectInput(in); + return in; + } + + private static final Pattern UNSAFE_PROJECT_NAME = + Pattern.compile("[^a-zA-Z0-9._/-]+"); + + protected Git git() { + return testRepo.git(); + } + + protected InMemoryRepository repo() { + return testRepo.getRepository(); + } + + /** + * Return a resource name scoped to this test method. + * <p> + * Test methods in a single class by default share a running server. For any + * resource name you require to be unique to a test method, wrap it in a call + * to this method. + * + * @param name resource name (group, project, topic, etc.) + * @return name prefixed by a string unique to this test method. + */ + protected String name(String name) { + return resourcePrefix + name; + } + + protected Project.NameKey createProject(String nameSuffix) + throws RestApiException { + return createProject(nameSuffix, null); + } + + protected Project.NameKey createProject(String nameSuffix, + Project.NameKey parent) throws RestApiException { + // Default for createEmptyCommit should match TestProjectConfig. + return createProject(nameSuffix, parent, true); + } + + protected Project.NameKey createProject(String nameSuffix, + Project.NameKey parent, boolean createEmptyCommit) + throws RestApiException { + ProjectInput in = new ProjectInput(); + in.name = name(nameSuffix); + in.parent = parent != null ? parent.get() : null; + in.createEmptyCommit = createEmptyCommit; + return createProject(in); + } + + private Project.NameKey createProject(ProjectInput in) + throws RestApiException { + gApi.projects().create(in); + return new Project.NameKey(in.name); + } + + /** + * Modify a project input before creating the initial test project. + * + * @param in input; may be modified in place. + */ + protected void updateProjectInput(ProjectInput in) { + // Default implementation does nothing. + } + + protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p) + throws Exception { + return cloneProject(p, admin); + } + + protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p, + TestAccount testAccount) throws Exception { + InProcessProtocol.Context ctx = new InProcessProtocol.Context( + reviewDbProvider, identifiedUserFactory, testAccount.getId(), p); + Repository repo = repoManager.openRepository(p); + toClose.add(repo); + return GitUtil.cloneProject( + p, inProcessProtocol.register(ctx, repo).toString()); } private void afterTest() throws Exception { + Transport.unregister(inProcessProtocol); + for (Repository repo : toClose) { + repo.close(); + } db.close(); sshSession.close(); - server.stop(); - TempFileUtil.cleanup(); + if (server != commonServer) { + server.stop(); + } } - protected PushOneCommit.Result createChange() throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - return push.to(git, "refs/for/master"); + protected TestRepository<?>.CommitBuilder commitBuilder() throws Exception { + return testRepo.branch("HEAD").commit().insertChangeId(); + } + + protected TestRepository<?>.CommitBuilder amendBuilder() throws Exception { + ObjectId head = repo().getRef("HEAD").getObjectId(); + TestRepository<?>.CommitBuilder b = testRepo.amendRef("HEAD"); + Optional<String> id = GitUtil.getChangeId(testRepo, head); + // TestRepository behaves like "git commit --amend -m foo", which does not + // preserve an existing Change-Id. Tests probably want this. + if (id.isPresent()) { + b.insertChangeId(id.get().substring(1)); + } else { + b.insertChangeId(); + } + return b; + } + + protected PushOneCommit.Result createChange() throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result result = push.to("refs/for/master"); + result.assertOkStatus(); + return result; } private static final List<Character> RANDOM = Chars.asList(new char[]{'a','b','c','d','e','f','g','h'}); protected PushOneCommit.Result amendChange(String changeId) - throws GitAPIException, IOException { + throws Exception { return amendChange(changeId, "refs/for/master"); } protected PushOneCommit.Result amendChange(String changeId, String ref) - throws GitAPIException, IOException { + throws Exception { Collections.shuffle(RANDOM); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, new String(Chars.toArray(RANDOM)), changeId); - return push.to(git, ref); - } - - protected ChangeInfo getChange(String changeId, ListChangesOption... options) - throws IOException { - return getChange(adminSession, changeId, options); - } - - protected ChangeInfo getChange(RestSession session, String changeId, - ListChangesOption... options) throws IOException { - String q = options.length > 0 ? "?o=" + Joiner.on("&o=").join(options) : ""; - RestResponse r = session.get("/changes/" + changeId + q); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), ChangeInfo.class); + return push.to(ref); } protected ChangeInfo info(String id) @@ -265,9 +414,8 @@ protected ChangeInfo get(String id, ListChangesOption... options) throws RestApiException { - EnumSet<ListChangesOption> s = EnumSet.noneOf(ListChangesOption.class); - s.addAll(Arrays.asList(options)); - return gApi.changes().id(id).get(s); + return gApi.changes().id(id).get( + Sets.newEnumSet(Arrays.asList(options), ListChangesOption.class)); } protected List<ChangeInfo> query(String q) throws RestApiException { @@ -283,6 +431,10 @@ return atrScope.set(newRequestContext(account)); } + protected Context setApiUserAnonymous() { + return atrScope.newContext(reviewDbProvider, null, anonymousUser.get()); + } + protected static Gson newGson() { return OutputFormat.JSON_COMPACT.newGson(); } @@ -300,10 +452,31 @@ saveProjectConfig(project, cfg); } - protected void allowGlobalCapability(String capabilityName, - AccountGroup.UUID id) throws Exception { + protected void allowGlobalCapabilities(AccountGroup.UUID id, + String... capabilityNames) throws Exception { + allowGlobalCapabilities(id, Arrays.asList(capabilityNames)); + } + + protected void allowGlobalCapabilities(AccountGroup.UUID id, + Iterable<String> capabilityNames) throws Exception { ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - Util.allow(cfg, capabilityName, id); + for (String capabilityName : capabilityNames) { + Util.allow(cfg, capabilityName, id); + } + saveProjectConfig(allProjects, cfg); + } + + protected void removeGlobalCapabilities(AccountGroup.UUID id, + String... capabilityNames) throws Exception { + removeGlobalCapabilities(id, Arrays.asList(capabilityNames)); + } + + protected void removeGlobalCapabilities(AccountGroup.UUID id, + Iterable<String> capabilityNames) throws Exception { + ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + for (String capabilityName : capabilityNames) { + Util.remove(cfg, capabilityName, id); + } saveProjectConfig(allProjects, cfg); } @@ -352,9 +525,22 @@ saveProjectConfig(project, cfg); } - protected PushOneCommit.Result pushTo(String ref) throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - return push.to(git, ref); + protected PushOneCommit.Result pushTo(String ref) throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + return push.to(ref); + } + + protected void approve(String id) throws Exception { + gApi.changes() + .id(id) + .revision("current") + .review(ReviewInput.approve()); + } + + protected Map<String, ActionInfo> getActions(String id) throws Exception { + return gApi.changes() + .id(id) + .revision(1) + .actions(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java index 2a578c2..0ff4709 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -41,7 +41,7 @@ private static final Key<RequestScopedReviewDbProvider> DB_KEY = Key.get(RequestScopedReviewDbProvider.class); - public class Context implements RequestContext { + public static class Context implements RequestContext { private final RequestCleanup cleanup = new RequestCleanup(); private final Map<Key<?>, Object> map = Maps.newHashMap(); private final SchemaFactory<ReviewDb> schemaFactory; @@ -162,6 +162,21 @@ return old; } + public Context disableDb() { + Context old = current.get(); + SchemaFactory<ReviewDb> sf = new SchemaFactory<ReviewDb>() { + @Override + public ReviewDb open() { + return new DisabledReviewDb(); + } + }; + Context ctx = new Context(sf, old.session, old.user, old.created); + + current.set(ctx); + local.setContext(ctx); + return old; + } + /** Returns exactly one instance per command executed. */ static final Scope REQUEST = new Scope() { @Override
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java index a376332..f247463 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -14,6 +14,8 @@ package com.google.gerrit.acceptance; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.gerrit.common.TimeUtil; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountExternalId; @@ -28,6 +30,7 @@ import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Inject; +import com.google.inject.Singleton; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; @@ -36,8 +39,12 @@ import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +@Singleton public class AccountCreator { + private final Map<String, TestAccount> accounts; private SchemaFactory<ReviewDb> reviewDbProvider; private GroupCache groupCache; @@ -49,6 +56,7 @@ AccountCreator(SchemaFactory<ReviewDb> schema, GroupCache groupCache, SshKeyCache sshKeyCache, AccountCache accountCache, AccountByEmailCache byEmailCache) { + accounts = new HashMap<>(); reviewDbProvider = schema; this.groupCache = groupCache; this.sshKeyCache = sshKeyCache; @@ -56,9 +64,13 @@ this.byEmailCache = byEmailCache; } - public TestAccount create(String username, String email, String fullName, - String... groups) + public synchronized TestAccount create(String username, String email, + String fullName, String... groups) throws OrmException, UnsupportedEncodingException, JSchException { + TestAccount account = accounts.get(username); + if (account != null) { + return account; + } ReviewDb db = reviewDbProvider.open(); try { Account.Id id = new Account.Id(db.nextAccountId()); @@ -99,7 +111,10 @@ accountCache.evictByUsername(username); byEmailCache.evict(email); - return new TestAccount(id, username, email, fullName, sshKey, httpPass); + account = + new TestAccount(id, username, email, fullName, sshKey, httpPass); + accounts.put(username, account); + return account; } finally { db.close(); } @@ -137,6 +152,12 @@ return create("user2", "user2@example.com", "User2"); } + public TestAccount get(String username) { + return checkNotNull( + accounts.get(username), + "No TestAccount created for %s", username); + } + private AccountExternalId.Key getEmailKey(String email) { return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email); }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java index 07d0f50..b07ed30 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
@@ -15,11 +15,13 @@ package com.google.gerrit.acceptance; import com.google.common.base.Splitter; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.eclipse.jgit.lib.Config; import java.util.ArrayList; +import java.util.Arrays; class ConfigAnnotationParser { private static Splitter splitter = Splitter.on(".").trimResults(); @@ -45,9 +47,19 @@ private static void parseAnnotation(Config cfg, GerritConfig c) { ArrayList<String> l = Lists.newArrayList(splitter.split(c.name())); if (l.size() == 2) { - cfg.setString(l.get(0), null, l.get(1), c.value()); + if (!Strings.isNullOrEmpty(c.value())) { + cfg.setString(l.get(0), null, l.get(1), c.value()); + } else { + String[] values = c.values(); + cfg.setStringList(l.get(0), null, l.get(1), Arrays.asList(values)); + } } else if (l.size() == 3) { - cfg.setString(l.get(0), l.get(1), l.get(2), c.value()); + if (!Strings.isNullOrEmpty(c.value())) { + cfg.setString(l.get(0), l.get(1), l.get(2), c.value()); + } else { + cfg.setStringList(l.get(0), l.get(1), l.get(2), + Arrays.asList(c.value())); + } } else { throw new IllegalArgumentException( "GerritConfig.name must be of the format"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java new file mode 100644 index 0000000..44d3d7f --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java
@@ -0,0 +1,206 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance; + +import com.google.gerrit.reviewdb.server.AccountAccess; +import com.google.gerrit.reviewdb.server.AccountDiffPreferenceAccess; +import com.google.gerrit.reviewdb.server.AccountExternalIdAccess; +import com.google.gerrit.reviewdb.server.AccountGroupAccess; +import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess; +import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess; +import com.google.gerrit.reviewdb.server.AccountGroupMemberAccess; +import com.google.gerrit.reviewdb.server.AccountGroupMemberAuditAccess; +import com.google.gerrit.reviewdb.server.AccountGroupNameAccess; +import com.google.gerrit.reviewdb.server.AccountPatchReviewAccess; +import com.google.gerrit.reviewdb.server.AccountProjectWatchAccess; +import com.google.gerrit.reviewdb.server.AccountSshKeyAccess; +import com.google.gerrit.reviewdb.server.ChangeAccess; +import com.google.gerrit.reviewdb.server.ChangeMessageAccess; +import com.google.gerrit.reviewdb.server.PatchLineCommentAccess; +import com.google.gerrit.reviewdb.server.PatchSetAccess; +import com.google.gerrit.reviewdb.server.PatchSetAncestorAccess; +import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.reviewdb.server.SchemaVersionAccess; +import com.google.gerrit.reviewdb.server.StarredChangeAccess; +import com.google.gerrit.reviewdb.server.SubmoduleSubscriptionAccess; +import com.google.gerrit.reviewdb.server.SystemConfigAccess; +import com.google.gwtorm.server.Access; +import com.google.gwtorm.server.StatementExecutor; + +/** ReviewDb that is disabled for testing. */ +class DisabledReviewDb implements ReviewDb { + private static final String MESSAGE = "ReviewDb is disabled for this test"; + + @Override + public void close() { + // Do nothing. + } + + @Override + public void commit() { + throw new AssertionError(MESSAGE); + } + + @Override + public void rollback() { + throw new AssertionError(MESSAGE); + } + + @Override + public void updateSchema(StatementExecutor e) { + throw new AssertionError(MESSAGE); + } + + @Override + public void pruneSchema(StatementExecutor e) { + throw new AssertionError(MESSAGE); + } + + @Override + public Access<?, ?>[] allRelations() { + throw new AssertionError(MESSAGE); + } + + @Override + public SchemaVersionAccess schemaVersion() { + throw new AssertionError(MESSAGE); + } + + @Override + public SystemConfigAccess systemConfig() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountAccess accounts() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountExternalIdAccess accountExternalIds() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountSshKeyAccess accountSshKeys() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupAccess accountGroups() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupNameAccess accountGroupNames() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupMemberAccess accountGroupMembers() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupMemberAuditAccess accountGroupMembersAudit() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountDiffPreferenceAccess accountDiffPreferences() { + throw new AssertionError(MESSAGE); + } + + @Override + public StarredChangeAccess starredChanges() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountProjectWatchAccess accountProjectWatches() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountPatchReviewAccess accountPatchReviews() { + throw new AssertionError(MESSAGE); + } + + @Override + public ChangeAccess changes() { + throw new AssertionError(MESSAGE); + } + + @Override + public PatchSetApprovalAccess patchSetApprovals() { + throw new AssertionError(MESSAGE); + } + + @Override + public ChangeMessageAccess changeMessages() { + throw new AssertionError(MESSAGE); + } + + @Override + public PatchSetAccess patchSets() { + throw new AssertionError(MESSAGE); + } + + @Override + public PatchSetAncestorAccess patchSetAncestors() { + throw new AssertionError(MESSAGE); + } + + @Override + public PatchLineCommentAccess patchComments() { + throw new AssertionError(MESSAGE); + } + + @Override + public SubmoduleSubscriptionAccess submoduleSubscriptions() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupByIdAccess accountGroupById() { + throw new AssertionError(MESSAGE); + } + + @Override + public AccountGroupByIdAudAccess accountGroupByIdAud() { + throw new AssertionError(MESSAGE); + } + + @Override + public int nextAccountId() { + throw new AssertionError(MESSAGE); + } + + @Override + public int nextAccountGroupId() { + throw new AssertionError(MESSAGE); + } + + @Override + public int nextChangeId() { + throw new AssertionError(MESSAGE); + } + + @Override + public int nextChangeMessageId() { + throw new AssertionError(MESSAGE); + } +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java index 5cb1229..4b956a2 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
@@ -24,5 +24,6 @@ @Retention(RUNTIME) public @interface GerritConfig { String name(); - String value(); + String value() default ""; + String[] values() default ""; }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java index 3be8195..a721221 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -14,15 +14,22 @@ package com.google.gerrit.acceptance; +import com.google.auto.value.AutoValue; +import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; +import com.google.gerrit.common.Nullable; import com.google.gerrit.lucene.LuceneIndexModule; import com.google.gerrit.pgm.Daemon; import com.google.gerrit.pgm.Init; import com.google.gerrit.server.config.FactoryModule; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.git.AsyncReceiveCommits; +import com.google.gerrit.server.git.SubmoduleOp; import com.google.gerrit.server.index.ChangeSchemas; +import com.google.gerrit.server.ssh.NoSshModule; import com.google.gerrit.server.util.SocketUtil; import com.google.gerrit.server.util.SystemLog; +import com.google.gerrit.testutil.FakeEmailSender; import com.google.gerrit.testutil.TempFileUtil; import com.google.inject.Injector; import com.google.inject.Key; @@ -47,10 +54,55 @@ import java.util.concurrent.TimeUnit; public class GerritServer { + @AutoValue + abstract static class Description { + static Description forTestClass(org.junit.runner.Description testDesc, + String configName) { + return new AutoValue_GerritServer_Description( + configName, + true, // @UseLocalDisk is only valid on methods. + testDesc.getTestClass().getAnnotation(NoHttpd.class) == null, + null, // @GerritConfig is only valid on methods. + null); // @GerritConfigs is only valid on methods. + + } + + static Description forTestMethod(org.junit.runner.Description testDesc, + String configName) { + return new AutoValue_GerritServer_Description( + configName, + testDesc.getAnnotation(UseLocalDisk.class) == null, + testDesc.getAnnotation(NoHttpd.class) == null + && testDesc.getTestClass().getAnnotation(NoHttpd.class) == null, + testDesc.getAnnotation(GerritConfig.class), + testDesc.getAnnotation(GerritConfigs.class)); + } + + @Nullable abstract String configName(); + abstract boolean memory(); + abstract boolean httpd(); + @Nullable abstract GerritConfig config(); + @Nullable abstract GerritConfigs configs(); + + private Config buildConfig(Config baseConfig) { + if (configs() != null && config() != null) { + throw new IllegalStateException( + "Use either @GerritConfigs or @GerritConfig not both"); + } + if (configs() != null) { + return ConfigAnnotationParser.parse(baseConfig, configs()); + } else if (config() != null) { + return ConfigAnnotationParser.parse(baseConfig, config()); + } else { + return baseConfig; + } + } + } /** Returns fully started Gerrit server */ - static GerritServer start(Config cfg, boolean memory, boolean enableHttpd) + static GerritServer start(Description desc, Config baseConfig) throws Exception { + Config cfg = desc.buildConfig(baseConfig); Logger.getLogger("com.google.gerrit").setLevel(Level.DEBUG); final CyclicBarrier serverStarted = new CyclicBarrier(2); final Daemon daemon = new Daemon(new Runnable() { @@ -65,10 +117,11 @@ } } }); + daemon.setEmailModuleForTesting(new FakeEmailSender.Module()); final File site; ExecutorService daemonService = null; - if (memory) { + if (desc.memory()) { site = null; mergeTestConfig(cfg); // Set the log4j configuration to an invalid one to prevent system logs @@ -78,10 +131,9 @@ cfg.setBoolean("sshd", null, "requestLog", false); cfg.setBoolean("index", "lucene", "testInmemory", true); cfg.setString("gitweb", null, "cgi", ""); - daemon.setEnableHttpd(enableHttpd); + daemon.setEnableHttpd(desc.httpd()); daemon.setLuceneModule(new LuceneIndexModule( - ChangeSchemas.getLatest().getVersion(), - Runtime.getRuntime().availableProcessors(), null)); + ChangeSchemas.getLatest().getVersion(), 0, null)); daemon.setDatabaseForTesting(ImmutableList.<Module>of( new InMemoryTestingDatabaseModule(cfg))); daemon.start(); @@ -105,7 +157,7 @@ } Injector i = createTestInjector(daemon); - return new GerritServer(i, daemon, daemonService); + return new GerritServer(desc, i, daemon, daemonService); } private static File initSite(Config base) throws Exception { @@ -138,9 +190,15 @@ cfg.setBoolean("sshd", null, "testUseInsecureRandom", true); cfg.setString("cache", null, "directory", null); cfg.setString("gerrit", null, "basePath", "git"); - cfg.setBoolean("sendemail", null, "enable", false); + cfg.setBoolean("sendemail", null, "enable", true); + cfg.setInt("sendemail", null, "threadPoolSize", 0); cfg.setInt("cache", "projects", "checkFrequency", 0); cfg.setInt("plugins", null, "checkFrequency", 0); + + cfg.setInt("sshd", null, "threads", 1); + cfg.setInt("sshd", null, "commandStartThreads", 1); + cfg.setInt("receive", null, "threadPoolSize", 1); + cfg.setInt("index", null, "threads", 1); } private static Injector createTestInjector(Daemon daemon) throws Exception { @@ -150,6 +208,10 @@ protected void configure() { bind(AccountCreator.class); factory(PushOneCommit.Factory.class); + factory(SubmoduleOp.Factory.class); + install(InProcessProtocol.module()); + install(new NoSshModule()); + install(new AsyncReceiveCommits.Module()); } }; return sysInjector.createChildInjector(module); @@ -167,6 +229,8 @@ return InetAddress.getLoopbackAddress(); } + private final Description desc; + private Daemon daemon; private ExecutorService daemonService; private Injector testInjector; @@ -174,8 +238,9 @@ private InetSocketAddress sshdAddress; private InetSocketAddress httpAddress; - private GerritServer(Injector testInjector, Daemon daemon, + private GerritServer(Description desc, Injector testInjector, Daemon daemon, ExecutorService daemonService) { + this.desc = desc; this.testInjector = testInjector; this.daemon = daemon; this.daemonService = daemonService; @@ -207,6 +272,10 @@ return testInjector; } + Description getDescription() { + return desc; + } + void stop() throws Exception { daemon.getLifecycleManager().stop(); if (daemonService != null) { @@ -216,4 +285,9 @@ } RepositoryCache.clear(); } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).addValue(desc).toString(); + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java index dee36ef..e8f8925 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GitUtil.java
@@ -14,46 +14,43 @@ package com.google.gerrit.acceptance; -import static com.google.common.base.Preconditions.checkState; - +import com.google.common.base.Optional; import com.google.common.collect.Iterables; +import com.google.common.primitives.Ints; import com.google.gerrit.common.FooterConstants; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.testutil.TempFileUtil; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; -import org.eclipse.jgit.api.AddCommand; -import org.eclipse.jgit.api.CheckoutCommand; -import org.eclipse.jgit.api.CloneCommand; -import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.FetchCommand; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.PushCommand; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.JschConfigSessionFactory; import org.eclipse.jgit.transport.OpenSshConfig.Host; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.SshSessionFactory; -import org.eclipse.jgit.util.ChangeIdUtil; import org.eclipse.jgit.util.FS; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; public class GitUtil { + private static final AtomicInteger testRepoCount = new AtomicInteger(); + private static final int TEST_REPO_WINDOW_DAYS = 2; public static void initSsh(final TestAccount a) { final Properties config = new Properties(); @@ -77,131 +74,75 @@ }); } - public static void createProject(SshSession s, String name) - throws JSchException, IOException { - createProject(s, name, null); + /** + * Create a new {@link TestRepository} with a distinct commit clock. + * <p> + * It is very easy for tests to create commits with identical subjects and + * trees; if such commits also have identical authors/committers, then the + * computed Change-Id is identical as well. Tests may generally assume that + * Change-Ids are unique, so to ensure this, we provision TestRepository + * instances with non-overlapping commit clock times. + * <p> + * Space test repos 1 day apart, which allows for about 86k ticks per repo + * before overlapping, and about 8k instances per process before hitting + * JGit's year 2038 limit. + * + * @param repo repository to wrap. + * @return wrapped test repository with distinct commit time space. + */ + public static <R extends Repository> TestRepository<R> newTestRepository( + R repo) throws IOException { + TestRepository<R> tr = new TestRepository<>(repo); + tr.tick(Ints.checkedCast(TimeUnit.SECONDS.convert( + testRepoCount.getAndIncrement() * TEST_REPO_WINDOW_DAYS, + TimeUnit.DAYS))); + return tr; } - public static void createProject(SshSession s, String name, Project.NameKey parent) - throws JSchException, IOException { - createProject(s, name, parent, true); - } - - public static void createProject(SshSession s, String name, - Project.NameKey parent, boolean emptyCommit) - throws JSchException, IOException { - StringBuilder b = new StringBuilder(); - b.append("gerrit create-project"); - if (emptyCommit) { - b.append(" --empty-commit"); + public static TestRepository<InMemoryRepository> cloneProject( + Project.NameKey project, String uri) throws Exception { + DfsRepositoryDescription desc = + new DfsRepositoryDescription("clone of " + project.get()); + InMemoryRepository dest = new InMemoryRepository.Builder() + .setRepositoryDescription(desc) + // SshTransport depends on a real FS to read ~/.ssh/config, but + // InMemoryRepository by default uses a null FS. + // TODO(dborowitz): Remove when we no longer depend on SSH. + .setFS(FS.detect()) + .build(); + Config cfg = dest.getConfig(); + cfg.setString("remote", "origin", "url", uri); + cfg.setString("remote", "origin", "fetch", + "+refs/heads/*:refs/remotes/origin/*"); + TestRepository<InMemoryRepository> testRepo = newTestRepository(dest); + FetchResult result = testRepo.git().fetch().setRemote("origin").call(); + String originMaster = "refs/remotes/origin/master"; + if (result.getTrackingRefUpdate(originMaster) != null) { + testRepo.reset(originMaster); } - b.append(" --name \""); - b.append(name); - b.append("\""); - if (parent != null) { - b.append(" --parent \""); - b.append(parent.get()); - b.append("\""); - } - s.exec(b.toString()); - if (s.hasError()) { - throw new IllegalStateException( - "gerrit create-project returned error: " + s.getError()); - } + return testRepo; } - public static Git cloneProject(String url) throws GitAPIException, IOException { - return cloneProject(url, true); + public static TestRepository<InMemoryRepository> cloneProject( + Project.NameKey project, SshSession sshSession) throws Exception { + return cloneProject(project, sshSession.getUrl() + "/" + project.get()); } - public static Git cloneProject(String url, boolean checkout) throws GitAPIException, IOException { - final File gitDir = TempFileUtil.createTempDirectory(); - final CloneCommand cloneCmd = Git.cloneRepository(); - cloneCmd.setURI(url); - cloneCmd.setDirectory(gitDir); - cloneCmd.setNoCheckout(!checkout); - return cloneCmd.call(); - } - - public static void add(Git git, String path, String content) - throws GitAPIException, IOException { - File f = new File(git.getRepository().getDirectory().getParentFile(), path); - File p = f.getParentFile(); - if (!p.exists() && !p.mkdirs()) { - throw new IOException("failed to create dir: " + p.getAbsolutePath()); - } - FileOutputStream s = new FileOutputStream(f); - BufferedWriter out = new BufferedWriter( - new OutputStreamWriter(s, StandardCharsets.UTF_8)); - try { - out.write(content); - } finally { - out.close(); - } - - final AddCommand addCmd = git.add(); - addCmd.addFilepattern(path); - addCmd.call(); - } - - public static void rm(Git gApi, String path) + public static void fetch(TestRepository<?> testRepo, String spec) throws GitAPIException { - gApi.rm() - .addFilepattern(path) - .call(); - } - - public static Commit createCommit(Git git, PersonIdent i, String msg) - throws GitAPIException { - return createCommit(git, i, msg, null); - } - - public static Commit amendCommit(Git git, PersonIdent i, String msg, String changeId) - throws GitAPIException { - msg = ChangeIdUtil.insertId(msg, ObjectId.fromString(changeId.substring(1))); - return createCommit(git, i, msg, changeId); - } - - private static Commit createCommit(Git git, PersonIdent i, String msg, - String changeId) throws GitAPIException { - - final CommitCommand commitCmd = git.commit(); - commitCmd.setAmend(changeId != null); - commitCmd.setAuthor(i); - commitCmd.setCommitter(i); - commitCmd.setMessage(msg); - commitCmd.setInsertChangeId(changeId == null); - - RevCommit c = commitCmd.call(); - - List<String> ids = c.getFooterLines(FooterConstants.CHANGE_ID); - checkState(ids.size() >= 1, - "No Change-Id found in new commit:\n%s", c.getFullMessage()); - changeId = ids.get(ids.size() - 1); - - return new Commit(c, changeId); - } - - public static void fetch(Git git, String spec) throws GitAPIException { - FetchCommand fetch = git.fetch(); + FetchCommand fetch = testRepo.git().fetch(); fetch.setRefSpecs(new RefSpec(spec)); fetch.call(); } - public static void checkout(Git git, String name) throws GitAPIException { - CheckoutCommand checkout = git.checkout(); - checkout.setName(name); - checkout.call(); + public static PushResult pushHead(TestRepository<?> testRepo, String ref, + boolean pushTags) throws GitAPIException { + return pushHead(testRepo, ref, pushTags, false); } - public static PushResult pushHead(Git git, String ref, boolean pushTags) - throws GitAPIException { - return pushHead(git, ref, pushTags, false); - } - - public static PushResult pushHead(Git git, String ref, boolean pushTags, - boolean force) throws GitAPIException { - PushCommand pushCmd = git.push(); + public static PushResult pushHead(TestRepository<?> testRepo, String ref, + boolean pushTags, boolean force) throws GitAPIException { + PushCommand pushCmd = testRepo.git().push(); pushCmd.setForce(force); pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref)); if (pushTags) { @@ -211,21 +152,14 @@ return Iterables.getOnlyElement(r); } - public static class Commit { - private final RevCommit commit; - private final String changeId; - - Commit(RevCommit commit, String changeId) { - this.commit = commit; - this.changeId = changeId; + public static Optional<String> getChangeId(TestRepository<?> tr, ObjectId id) + throws IOException { + RevCommit c = tr.getRevWalk().parseCommit(id); + tr.getRevWalk().parseBody(c); + List<String> ids = c.getFooterLines(FooterConstants.CHANGE_ID); + if (ids.isEmpty()) { + return Optional.absent(); } - - public RevCommit getCommit() { - return commit; - } - - public String getChangeId() { - return changeId; - } + return Optional.of(ids.get(ids.size() - 1)); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java index 8548b5c..8f4c2d4 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -43,7 +43,8 @@ import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; import org.eclipse.jgit.lib.Config; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; class InMemoryTestingDatabaseModule extends LifecycleModule { private final Config cfg; @@ -58,9 +59,10 @@ .annotatedWith(GerritServerConfig.class) .toInstance(cfg); - bind(File.class) + // TODO(dborowitz): Use jimfs. + bind(Path.class) .annotatedWith(SitePath.class) - .toInstance(new File("UNIT_TEST_GERRIT_SITE")); + .toInstance(Paths.get("UNIT_TEST_GERRIT_SITE")); bind(GitRepositoryManager.class) .toInstance(new InMemoryRepositoryManager());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java new file mode 100644 index 0000000..c02b9e5 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -0,0 +1,352 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance; + +import com.google.common.collect.Lists; +import com.google.gerrit.acceptance.InProcessProtocol.Context; +import com.google.gerrit.common.data.Capable; +import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.AccessPath; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.RemotePeer; +import com.google.gerrit.server.RequestCleanup; +import com.google.gerrit.server.config.GerritRequestModule; +import com.google.gerrit.server.config.RequestScopedReviewDbProvider; +import com.google.gerrit.server.git.AsyncReceiveCommits; +import com.google.gerrit.server.git.ChangeCache; +import com.google.gerrit.server.git.ReceiveCommits; +import com.google.gerrit.server.git.ReceivePackInitializer; +import com.google.gerrit.server.git.TagCache; +import com.google.gerrit.server.git.TransferConfig; +import com.google.gerrit.server.git.VisibleRefFilter; +import com.google.gerrit.server.git.validators.UploadValidators; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.util.RequestContext; +import com.google.gerrit.server.util.RequestScopePropagator; +import com.google.gerrit.server.util.ThreadLocalRequestContext; +import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator; +import com.google.gwtorm.server.SchemaFactory; +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.OutOfScopeException; +import com.google.inject.Provider; +import com.google.inject.Provides; +import com.google.inject.Scope; +import com.google.inject.servlet.RequestScoped; +import com.google.inject.util.Providers; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.PostReceiveHook; +import org.eclipse.jgit.transport.PostReceiveHookChain; +import org.eclipse.jgit.transport.PreUploadHook; +import org.eclipse.jgit.transport.PreUploadHookChain; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.TestProtocol; +import org.eclipse.jgit.transport.UploadPack; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.UploadPackFactory; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class InProcessProtocol extends TestProtocol<Context> { + static Module module() { + return new AbstractModule() { + @Override + public void configure() { + install(new GerritRequestModule()); + bind(RequestScopePropagator.class).to(Propagator.class); + bindScope(RequestScoped.class, InProcessProtocol.REQUEST); + } + + @Provides + @RemotePeer + SocketAddress getSocketAddress() { + // TODO(dborowitz): Could potentially fake this with thread ID or + // something. + throw new OutOfScopeException("No remote peer in acceptance tests"); + } + }; + } + + private static final Scope REQUEST = new Scope() { + @Override + public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) { + return new Provider<T>() { + @Override + public T get() { + Context ctx = current.get(); + if (ctx == null) { + throw new OutOfScopeException("Not in TestProtocol scope"); + } + return ctx.get(key, creator); + } + + @Override + public String toString() { + return String.format("%s[%s]", creator, REQUEST); + } + }; + } + + @Override + public String toString() { + return "InProcessProtocol.REQUEST"; + } + }; + + private static class Propagator + extends ThreadLocalRequestScopePropagator<Context> { + @Inject + Propagator(ThreadLocalRequestContext local, + Provider<RequestScopedReviewDbProvider> dbProviderProvider) { + super(REQUEST, current, local, dbProviderProvider); + } + + @Override + protected Context continuingContext(Context ctx) { + return ctx.newContinuingContext(); + } + } + + private static final ThreadLocal<Context> current = new ThreadLocal<>(); + + // TODO(dborowitz): Merge this with AcceptanceTestRequestScope. + /** + * Multi-purpose session/context object. + * <p> + * Confusingly, Gerrit has two ideas of what a "context" object is: + * one for Guice {@link RequestScoped}, and one for its own simplified + * version of request scoping using {@link ThreadLocalRequestContext}. + * This class provides both, in essence just delegating the {@code + * ThreadLocalRequestContext} scoping to the Guice scoping mechanism. + * <p> + * It is also used as the session type for {@code UploadPackFactory} and + * {@code ReceivePackFactory}, since, after all, it encapsulates all the + * information about a single request. + */ + static class Context implements RequestContext { + private static final Key<RequestScopedReviewDbProvider> DB_KEY = + Key.get(RequestScopedReviewDbProvider.class); + private static final Key<RequestCleanup> RC_KEY = + Key.get(RequestCleanup.class); + private static final Key<CurrentUser> USER_KEY = Key.get(CurrentUser.class); + + private final SchemaFactory<ReviewDb> schemaFactory; + private final IdentifiedUser.GenericFactory userFactory; + private final Account.Id accountId; + private final Project.NameKey project; + private final RequestCleanup cleanup; + private final Map<Key<?>, Object> map; + + Context(SchemaFactory<ReviewDb> schemaFactory, + IdentifiedUser.GenericFactory userFactory, + Account.Id accountId, + Project.NameKey project) { + this.schemaFactory = schemaFactory; + this.userFactory = userFactory; + this.accountId = accountId; + this.project = project; + map = new HashMap<>(); + cleanup = new RequestCleanup(); + map.put(DB_KEY, + new RequestScopedReviewDbProvider( + schemaFactory, Providers.of(cleanup))); + map.put(RC_KEY, cleanup); + + IdentifiedUser user = userFactory.create(accountId); + user.setAccessPath(AccessPath.GIT); + map.put(USER_KEY, user); + } + + private Context newContinuingContext() { + return new Context(schemaFactory, userFactory, accountId, project); + } + + @Override + public CurrentUser getCurrentUser() { + return get(USER_KEY, null); + } + + @Override + public Provider<ReviewDb> getReviewDbProvider() { + return get(DB_KEY, null); + } + + private synchronized <T> T get(Key<T> key, Provider<T> creator) { + @SuppressWarnings("unchecked") + T t = (T) map.get(key); + if (t == null) { + t = creator.get(); + map.put(key, t); + } + return t; + } + } + + private static class Upload implements UploadPackFactory<Context> { + private final Provider<ReviewDb> dbProvider; + private final Provider<CurrentUser> userProvider; + private final TagCache tagCache; + private final ChangeCache changeCache; + private final ProjectControl.GenericFactory projectControlFactory; + private final TransferConfig transferConfig; + private final DynamicSet<PreUploadHook> preUploadHooks; + private final UploadValidators.Factory uploadValidatorsFactory; + private final ThreadLocalRequestContext threadContext; + + @Inject + Upload( + Provider<ReviewDb> dbProvider, + Provider<CurrentUser> userProvider, + TagCache tagCache, + ChangeCache changeCache, + ProjectControl.GenericFactory projectControlFactory, + TransferConfig transferConfig, + DynamicSet<PreUploadHook> preUploadHooks, + UploadValidators.Factory uploadValidatorsFactory, + ThreadLocalRequestContext threadContext) { + this.dbProvider = dbProvider; + this.userProvider = userProvider; + this.tagCache = tagCache; + this.changeCache = changeCache; + this.projectControlFactory = projectControlFactory; + this.transferConfig = transferConfig; + this.preUploadHooks = preUploadHooks; + this.uploadValidatorsFactory = uploadValidatorsFactory; + this.threadContext = threadContext; + } + + @Override + public UploadPack create(Context req, final Repository repo) + throws ServiceNotAuthorizedException { + // Set the request context, but don't bother unsetting, since we don't + // have an easy way to run code when this instance is done being used. + // Each operation is run in its own thread, so we don't need to recover + // its original context anyway. + threadContext.setContext(req); + current.set(req); + try { + ProjectControl ctl = projectControlFactory.controlFor( + req.project, userProvider.get()); + if (!ctl.canRunUploadPack()) { + throw new ServiceNotAuthorizedException(); + } + + UploadPack up = new UploadPack(repo); + up.setPackConfig(transferConfig.getPackConfig()); + up.setTimeout(transferConfig.getTimeout()); + + if (!ctl.allRefsAreVisible()) { + up.setAdvertiseRefsHook(new VisibleRefFilter( + tagCache, changeCache, repo, ctl, dbProvider.get(), true)); + } + List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks); + hooks.add(uploadValidatorsFactory.create( + ctl.getProject(), repo, "localhost-test")); + up.setPreUploadHook(PreUploadHookChain.newChain(hooks)); + return up; + } catch (NoSuchProjectException | IOException e) { + throw new RuntimeException(e); + } + } + } + + private static class Receive implements ReceivePackFactory<Context> { + private final Provider<CurrentUser> userProvider; + private final ProjectControl.GenericFactory projectControlFactory; + private final AsyncReceiveCommits.Factory factory; + private final TransferConfig config; + private final DynamicSet<ReceivePackInitializer> receivePackInitializers; + private final DynamicSet<PostReceiveHook> postReceiveHooks; + private final ThreadLocalRequestContext threadContext; + + @Inject + Receive( + Provider<CurrentUser> userProvider, + ProjectControl.GenericFactory projectControlFactory, + AsyncReceiveCommits.Factory factory, + TransferConfig config, + DynamicSet<ReceivePackInitializer> receivePackInitializers, + DynamicSet<PostReceiveHook> postReceiveHooks, + ThreadLocalRequestContext threadContext) { + this.userProvider = userProvider; + this.projectControlFactory = projectControlFactory; + this.factory = factory; + this.config = config; + this.receivePackInitializers = receivePackInitializers; + this.postReceiveHooks = postReceiveHooks; + this.threadContext = threadContext; + } + + @Override + public ReceivePack create(final Context req, Repository db) + throws ServiceNotAuthorizedException { + // Set the request context, but don't bother unsetting, since we don't + // have an easy way to run code when this instance is done being used. + // Each operation is run in its own thread, so we don't need to recover + // its original context anyway. + threadContext.setContext(req); + current.set(req); + try { + ProjectControl ctl = + projectControlFactory.controlFor(req.project, userProvider.get()); + if (!ctl.canRunReceivePack()) { + throw new ServiceNotAuthorizedException(); + } + + ReceiveCommits rc = factory.create(ctl, db).getReceiveCommits(); + ReceivePack rp = rc.getReceivePack(); + + Capable r = rc.canUpload(); + if (r != Capable.OK) { + throw new ServiceNotAuthorizedException(); + } + + IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser(); + rp.setRefLogIdent(user.newRefLogIdent()); + rp.setTimeout(config.getTimeout()); + rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit()); + + for (ReceivePackInitializer initializer : receivePackInitializers) { + initializer.init(ctl.getProject().getNameKey(), rp); + } + + rp.setPostReceiveHook(PostReceiveHookChain.newChain( + Lists.newArrayList(postReceiveHooks))); + return rp; + } catch (NoSuchProjectException | IOException e) { + throw new RuntimeException(e); + } + } + } + + @Inject + InProcessProtocol(Upload uploadPackFactory, + Receive receivePackFactory) { + super(uploadPackFactory, receivePackFactory); + } +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java index 6cd8031..f2c51e1 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -15,17 +15,11 @@ package com.google.gerrit.acceptance; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.add; -import static com.google.gerrit.acceptance.GitUtil.amendCommit; -import static com.google.gerrit.acceptance.GitUtil.createCommit; import static com.google.gerrit.acceptance.GitUtil.pushHead; -import com.google.common.base.Function; import com.google.common.base.Strings; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.gerrit.acceptance.GitUtil.Commit; import com.google.gerrit.common.Nullable; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; @@ -40,12 +34,8 @@ import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.TagCommand; -import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.api.errors.InvalidTagNameException; -import org.eclipse.jgit.api.errors.NoHeadException; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; @@ -53,10 +43,6 @@ import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.RemoteRefUpdate.Status; -import java.io.IOException; -import java.util.Arrays; -import java.util.Set; - public class PushOneCommit { public static final String SUBJECT = "test commit"; public static final String FILE_NAME = "a.txt"; @@ -65,11 +51,13 @@ public interface Factory { PushOneCommit create( ReviewDb db, - PersonIdent i); + PersonIdent i, + TestRepository<?> testRepo); PushOneCommit create( ReviewDb db, PersonIdent i, + TestRepository<?> testRepo, @Assisted("subject") String subject, @Assisted("fileName") String fileName, @Assisted("content") String content); @@ -77,6 +65,7 @@ PushOneCommit create( ReviewDb db, PersonIdent i, + TestRepository<?> testRepo, @Assisted("subject") String subject, @Assisted("fileName") String fileName, @Assisted("content") String content, @@ -106,7 +95,7 @@ private final ApprovalsUtil approvalsUtil; private final Provider<InternalChangeQuery> queryProvider; private final ReviewDb db; - private final PersonIdent i; + private final TestRepository<?> testRepo; private final String subject; private final String fileName; @@ -115,14 +104,17 @@ private Tag tag; private boolean force; + private final TestRepository<?>.CommitBuilder commitBuilder; + @AssistedInject PushOneCommit(ChangeNotes.Factory notesFactory, ApprovalsUtil approvalsUtil, Provider<InternalChangeQuery> queryProvider, @Assisted ReviewDb db, - @Assisted PersonIdent i) { + @Assisted PersonIdent i, + @Assisted TestRepository<?> testRepo) throws Exception { this(notesFactory, approvalsUtil, queryProvider, - db, i, SUBJECT, FILE_NAME, FILE_CONTENT); + db, i, testRepo, SUBJECT, FILE_NAME, FILE_CONTENT); } @AssistedInject @@ -131,11 +123,12 @@ Provider<InternalChangeQuery> queryProvider, @Assisted ReviewDb db, @Assisted PersonIdent i, + @Assisted TestRepository<?> testRepo, @Assisted("subject") String subject, @Assisted("fileName") String fileName, - @Assisted("content") String content) { + @Assisted("content") String content) throws Exception { this(notesFactory, approvalsUtil, queryProvider, - db, i, subject, fileName, content, null); + db, i, testRepo, subject, fileName, content, null); } @AssistedInject @@ -144,42 +137,48 @@ Provider<InternalChangeQuery> queryProvider, @Assisted ReviewDb db, @Assisted PersonIdent i, + @Assisted TestRepository<?> testRepo, @Assisted("subject") String subject, @Assisted("fileName") String fileName, @Assisted("content") String content, - @Nullable @Assisted("changeId") String changeId) { + @Nullable @Assisted("changeId") String changeId) throws Exception { this.db = db; + this.testRepo = testRepo; this.notesFactory = notesFactory; this.approvalsUtil = approvalsUtil; this.queryProvider = queryProvider; - this.i = i; this.subject = subject; this.fileName = fileName; this.content = content; this.changeId = changeId; - } - - public Result to(Git git, String ref) throws GitAPIException, IOException { - add(git, fileName, content); - return execute(git, ref); - } - - public Result rm(Git git, String ref) throws GitAPIException { - GitUtil.rm(git, fileName); - return execute(git, ref); - } - - private Result execute(Git git, String ref) throws GitAPIException, - ConcurrentRefUpdateException, InvalidTagNameException, NoHeadException { - Commit c; if (changeId != null) { - c = amendCommit(git, i, subject, changeId); + commitBuilder = testRepo.amendRef("HEAD") + .insertChangeId(changeId.substring(1)); } else { - c = createCommit(git, i, subject); - changeId = c.getChangeId(); + commitBuilder = testRepo.branch("HEAD").commit().insertChangeId(); + } + commitBuilder.message(subject) + .author(i) + .committer(new PersonIdent(i, testRepo.getClock())); + } + + public Result to(String ref) throws Exception { + commitBuilder.add(fileName, content); + return execute(ref); + } + + public Result rm(String ref) throws Exception { + commitBuilder.rm(fileName); + return execute(ref); + } + + private Result execute(String ref) throws Exception { + RevCommit c = commitBuilder.create(); + if (changeId == null) { + changeId = GitUtil.getChangeId(testRepo, c).get(); } if (tag != null) { - TagCommand tagCommand = git.tag().setName(tag.name); + TagCommand tagCommand = testRepo.git().tag().setName(tag.name); if (tag instanceof AnnotatedTag) { AnnotatedTag annotatedTag = (AnnotatedTag)tag; tagCommand.setAnnotated(true) @@ -190,7 +189,8 @@ } tagCommand.call(); } - return new Result(ref, pushHead(git, ref, tag != null, force), c, subject); + return new Result(ref, pushHead(testRepo, ref, tag != null, force), c, + subject); } public void setTag(final Tag tag) { @@ -204,10 +204,10 @@ public class Result { private final String ref; private final PushResult result; - private final Commit commit; + private final RevCommit commit; private final String resSubj; - private Result(String ref, PushResult resSubj, Commit commit, + private Result(String ref, PushResult resSubj, RevCommit commit, String subject) { this.ref = ref; this.result = resSubj; @@ -217,7 +217,7 @@ public ChangeData getChange() throws OrmException { return Iterables.getOnlyElement( - queryProvider.get().byKeyPrefix(commit.getChangeId())); + queryProvider.get().byKeyPrefix(changeId)); } public PatchSet getPatchSet() throws OrmException { @@ -229,47 +229,33 @@ } public String getChangeId() { - return commit.getChangeId(); + return changeId; } public ObjectId getCommitId() { - return commit.getCommit().getId(); + return commit; } public RevCommit getCommit() { - return commit.getCommit(); + return commit; } public void assertChange(Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers) throws OrmException { Change c = getChange().change(); - assertThat(resSubj).isEqualTo(c.getSubject()); - assertThat(expectedStatus).isEqualTo(c.getStatus()); - assertThat(expectedTopic).isEqualTo(Strings.emptyToNull(c.getTopic())); + assertThat(c.getSubject()).isEqualTo(resSubj); + assertThat(c.getStatus()).isEqualTo(expectedStatus); + assertThat(Strings.emptyToNull(c.getTopic())).isEqualTo(expectedTopic); assertReviewers(c, expectedReviewers); } private void assertReviewers(Change c, TestAccount... expectedReviewers) throws OrmException { - Set<Account.Id> expectedReviewerIds = - Sets.newHashSet(Lists.transform(Arrays.asList(expectedReviewers), - new Function<TestAccount, Account.Id>() { - @Override - public Account.Id apply(TestAccount a) { - return a.id; - } - })); - - for (Account.Id accountId - : approvalsUtil.getReviewers(db, notesFactory.create(c)).values()) { - assertThat(expectedReviewerIds.remove(accountId)) - .named("unexpected reviewer " + accountId) - .isTrue(); - } - assertThat((Iterable<?>)expectedReviewerIds) - .named("missing reviewers: " + expectedReviewerIds) - .isEmpty(); + Iterable<Account.Id> actualIds = + approvalsUtil.getReviewers(db, notesFactory.create(c)).values(); + assertThat(actualIds).containsExactlyElementsIn( + Sets.newHashSet(TestAccount.ids(expectedReviewers))); } public void assertOkStatus() { @@ -282,10 +268,10 @@ private void assertStatus(Status expectedStatus, String expectedMessage) { RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref); - assertThat(expectedStatus) + assertThat(refUpdate.getStatus()) .named(message(refUpdate)) - .isEqualTo(refUpdate.getStatus()); - assertThat(expectedMessage).isEqualTo(refUpdate.getMessage()); + .isEqualTo(expectedStatus); + assertThat(refUpdate.getMessage()).isEqualTo(expectedMessage); } public void assertMessage(String expectedMessage) {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java index bf6f928..e06d31f 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
@@ -65,7 +65,20 @@ } public RestResponse put(String endPoint, Object content) throws IOException { + return putWithHeader(endPoint, null, content); + } + + public RestResponse putWithHeader(String endPoint, Header header) + throws IOException { + return putWithHeader(endPoint, header, null); + } + + public RestResponse putWithHeader(String endPoint, Header header, + Object content) throws IOException { HttpPut put = new HttpPut(url + "/a" + endPoint); + if (header != null) { + put.addHeader(header); + } if (content != null) { put.addHeader(new BasicHeader("Content-Type", "application/json")); put.setEntity(new StringEntity(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java index 701b337..794f832 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/SshSession.java
@@ -42,11 +42,12 @@ } @SuppressWarnings("resource") - public String exec(String command) throws JSchException, IOException { + public String exec(String command, InputStream opt) throws JSchException, + IOException { ChannelExec channel = (ChannelExec) getSession().openChannel("exec"); try { channel.setCommand(command); - channel.setInputStream(null); + channel.setInputStream(opt); InputStream in = channel.getInputStream(); channel.connect(); @@ -60,6 +61,20 @@ } } + public InputStream exec2(String command, InputStream opt) throws JSchException, + IOException { + ChannelExec channel = (ChannelExec) getSession().openChannel("exec"); + channel.setCommand(command); + channel.setInputStream(opt); + InputStream in = channel.getInputStream(); + channel.connect(); + return in; + } + + public String exec(String command) throws JSchException, IOException { + return exec(command, null); + } + public boolean hasError() { return error != null; }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java index bd5f19f..e7b5834 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestAccount.java
@@ -14,6 +14,8 @@ package com.google.gerrit.acceptance; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; import com.google.gerrit.reviewdb.client.Account; import com.jcraft.jsch.KeyPair; @@ -21,9 +23,38 @@ import org.eclipse.jgit.lib.PersonIdent; import java.io.ByteArrayOutputStream; - +import java.util.Arrays; public class TestAccount { + public static FluentIterable<Account.Id> ids( + Iterable<TestAccount> accounts) { + return FluentIterable.from(accounts) + .transform(new Function<TestAccount, Account.Id>() { + @Override + public Account.Id apply(TestAccount in) { + return in.id; + } + }); + } + + public static FluentIterable<Account.Id> ids(TestAccount... accounts) { + return ids(Arrays.asList(accounts)); + } + + public static FluentIterable<String> names(Iterable<TestAccount> accounts) { + return FluentIterable.from(accounts) + .transform(new Function<TestAccount, String>() { + @Override + public String apply(TestAccount in) { + return in.fullName; + } + }); + } + + public static FluentIterable<String> names(TestAccount... accounts) { + return names(Arrays.asList(accounts)); + } + public final Account.Id id; public final String username; public final String email;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java new file mode 100644 index 0000000..4ad37e2 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/TestProjectInput.java
@@ -0,0 +1,49 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.gerrit.extensions.client.InheritableBoolean; +import com.google.gerrit.extensions.client.SubmitType; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({METHOD}) +@Retention(RUNTIME) +public @interface TestProjectInput { + // Fields from ProjectInput for creating the project. + + String parent() default ""; + boolean createEmptyCommit() default true; + String description() default ""; + + // These may be null in a ProjectInput, but annotations do not allow null + // default values. Thus these defaults should match ProjectConfig. + SubmitType submitType() default SubmitType.MERGE_IF_NECESSARY; + InheritableBoolean useContributorAgreements() + default InheritableBoolean.INHERIT; + InheritableBoolean useSignedOffBy() default InheritableBoolean.INHERIT; + InheritableBoolean useContentMerge() default InheritableBoolean.INHERIT; + InheritableBoolean requireChangeId() default InheritableBoolean.INHERIT; + + + // Fields specific to acceptance test behavior. + + /** Username to use for initial clone, passed to {@link AccountCreator}. */ + String cloneAs() default "admin"; +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java index 8945a22d..aaf6192 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -22,6 +22,8 @@ import org.junit.Test; +import java.util.List; + public class AccountIT extends AbstractDaemonTest { @Test @@ -49,14 +51,31 @@ @Test public void starUnstarChange() throws Exception { PushOneCommit.Result r = createChange(); - String triplet = "p~master~" + r.getChangeId(); + String triplet = project.get() + "~master~" + r.getChangeId(); gApi.accounts() .self() .starChange(triplet); - assertThat(getChange(triplet).starred).isTrue(); + assertThat(info(triplet).starred).isTrue(); gApi.accounts() .self() .unstarChange(triplet); - assertThat(getChange(triplet).starred).isNull(); + assertThat(info(triplet).starred).isNull(); + } + + @Test + public void suggestAccounts() throws Exception { + String adminUsername = "admin"; + List<AccountInfo> result = gApi.accounts() + .suggestAccounts().withQuery(adminUsername).get(); + assertThat(result).hasSize(1); + assertThat(result.get(0).username).isEqualTo(adminUsername); + + List<AccountInfo> resultShortcutApi = gApi.accounts() + .suggestAccounts(adminUsername).get(); + assertThat(resultShortcutApi).hasSize(result.size()); + + List<AccountInfo> emptyResult = gApi.accounts() + .suggestAccounts("unknown").get(); + assertThat(emptyResult).isEmpty(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java index c13187f..9a58702 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -15,6 +15,11 @@ package com.google.gerrit.acceptance.api.change; import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME; +import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT; +import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; +import static com.google.gerrit.server.project.Util.category; +import static com.google.gerrit.server.project.Util.value; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -22,6 +27,8 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.common.data.LabelType; +import com.google.gerrit.common.data.Permission; import com.google.gerrit.extensions.api.changes.AddReviewerInput; import com.google.gerrit.extensions.api.changes.RebaseInput; import com.google.gerrit.extensions.api.changes.ReviewInput; @@ -33,11 +40,17 @@ import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.group.SystemGroupBackend; +import com.google.gerrit.server.project.Util; import org.eclipse.jgit.lib.Constants; import org.junit.Test; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -48,19 +61,19 @@ @Test public void get() throws Exception { PushOneCommit.Result r = createChange(); - String triplet = "p~master~" + r.getChangeId(); + String triplet = project.get() + "~master~" + r.getChangeId(); ChangeInfo c = info(triplet); assertThat(c.id).isEqualTo(triplet); - assertThat(c.project).isEqualTo("p"); + assertThat(c.project).isEqualTo(project.get()); assertThat(c.branch).isEqualTo("master"); assertThat(c.status).isEqualTo(ChangeStatus.NEW); assertThat(c.subject).isEqualTo("test commit"); assertThat(c.mergeable).isTrue(); assertThat(c.changeId).isEqualTo(r.getChangeId()); assertThat(c.created).isEqualTo(c.updated); - assertThat(c._number).is(1); + assertThat(c._number).isEqualTo(r.getChange().getId().get()); - assertThat(c.owner._accountId).is(admin.getId().get()); + assertThat(c.owner._accountId).isEqualTo(admin.getId().get()); assertThat(c.owner.name).isNull(); assertThat(c.owner.email).isNull(); assertThat(c.owner.username).isNull(); @@ -126,7 +139,7 @@ .revision(r3.getCommit().name()) .rebase(ri); PatchSet ps3 = r3.getPatchSet(); - assertThat(ps3.getId().get()).is(2); + assertThat(ps3.getId().get()).isEqualTo(2); // rebase r2 onto r3 (referenced by ref) ri.base = ps3.getId().toRefName(); @@ -135,7 +148,7 @@ .revision(r2.getCommit().name()) .rebase(ri); PatchSet ps2 = r2.getPatchSet(); - assertThat(ps2.getId().get()).is(2); + assertThat(ps2.getId().get()).isEqualTo(2); // rebase r1 onto r2 (referenced by commit) ri.base = ps2.getRevision().get(); @@ -144,7 +157,7 @@ .revision(r1.getCommit().name()) .rebase(ri); PatchSet ps1 = r1.getPatchSet(); - assertThat(ps1.getId().get()).is(2); + assertThat(ps1.getId().get()).isEqualTo(2); // rebase r1 onto r3 (referenced by change number) ri.base = String.valueOf(r3.getChange().getId().get()); @@ -152,7 +165,7 @@ .id(r1.getChangeId()) .revision(ps1.getRevision().get()) .rebase(ri); - assertThat(r1.getPatchSetId().get()).is(3); + assertThat(r1.getPatchSetId().get()).isEqualTo(3); } @Test(expected = ResourceConflictException.class) @@ -188,7 +201,7 @@ .id(r.getChangeId()) .addReviewer(in); - assertThat((Iterable<?>)getReviewers(r.getChangeId())) + assertThat(getReviewers(r.getChangeId())) .containsExactlyElementsIn(ImmutableSet.of(user.id)); } @@ -204,7 +217,7 @@ .revision(r.getCommit().name()) .submit(); - assertThat((Iterable<?>)getReviewers(r.getChangeId())) + assertThat(getReviewers(r.getChangeId())) .containsExactlyElementsIn(ImmutableSet.of(admin.getId())); AddReviewerInput in = new AddReviewerInput(); @@ -212,7 +225,7 @@ gApi.changes() .id(r.getChangeId()) .addReviewer(in); - assertThat((Iterable<?>)getReviewers(r.getChangeId())) + assertThat(getReviewers(r.getChangeId())) .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.id)); } @@ -233,38 +246,35 @@ @Test public void queryChangesNoQuery() throws Exception { - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = createChange(); + PushOneCommit.Result r = createChange(); List<ChangeInfo> results = gApi.changes().query().get(); - assertThat(results).hasSize(2); - assertThat(results.get(0).changeId).isEqualTo(r2.getChangeId()); - assertThat(results.get(1).changeId).isEqualTo(r1.getChangeId()); + assertThat(results.size()).isAtLeast(1); + List<Integer> ids = new ArrayList<>(results.size()); + for (int i = 0; i < results.size(); i++) { + ChangeInfo info = results.get(i); + if (i == 0) { + assertThat(info._number).isEqualTo(r.getChange().getId().get()); + } + assertThat(Change.Status.forChangeStatus(info.status).isOpen()).isTrue(); + ids.add(info._number); + } + assertThat(ids).contains(r.getChange().getId().get()); } @Test public void queryChangesNoResults() throws Exception { createChange(); - List<ChangeInfo> results = query("status:open"); - assertThat(results).hasSize(1); - results = query("status:closed"); - assertThat(results).isEmpty(); + assertThat(query("message:test")).isNotEmpty(); + assertThat(query("message:{" + getClass().getName() + "fhqwhgads}")) + .isEmpty(); } @Test - public void queryChangesOneTerm() throws Exception { - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = createChange(); - List<ChangeInfo> results = query("status:open"); - assertThat(results).hasSize(2); - assertThat(results.get(0).changeId).isEqualTo(r2.getChangeId()); - assertThat(results.get(1).changeId).isEqualTo(r1.getChangeId()); - } - - @Test - public void queryChangesMultipleTerms() throws Exception { + public void queryChanges() throws Exception { PushOneCommit.Result r1 = createChange(); createChange(); - List<ChangeInfo> results = query("status:open " + r1.getChangeId()); + List<ChangeInfo> results = + query("project:{" + project.get() + "} " + r1.getChangeId()); assertThat(Iterables.getOnlyElement(results).changeId) .isEqualTo(r1.getChangeId()); } @@ -283,7 +293,8 @@ public void queryChangesStart() throws Exception { PushOneCommit.Result r1 = createChange(); createChange(); - List<ChangeInfo> results = gApi.changes().query().withStart(1).get(); + List<ChangeInfo> results = gApi.changes() + .query("project:{" + project.get() + "}").withStart(1).get(); assertThat(Iterables.getOnlyElement(results).changeId) .isEqualTo(r1.getChangeId()); } @@ -293,7 +304,7 @@ PushOneCommit.Result r = createChange(); ChangeInfo result = Iterables.getOnlyElement(query(r.getChangeId())); assertThat(result.labels).isNull(); - assertThat((Iterable<?>)result.messages).isNull(); + assertThat(result.messages).isNull(); assertThat(result.revisions).isNull(); assertThat(result.actions).isNull(); } @@ -307,13 +318,13 @@ .get()); assertThat(Iterables.getOnlyElement(result.labels.keySet())) .isEqualTo("Code-Review"); - assertThat((Iterable<?>)result.messages).hasSize(1); + assertThat(result.messages).hasSize(1); assertThat(result.actions).isNotEmpty(); RevisionInfo rev = Iterables.getOnlyElement(result.revisions.values()); assertThat(rev._number).isEqualTo(r.getPatchSetId().get()); assertThat(rev.created).isNotNull(); - assertThat(rev.uploader._accountId).is(admin.getId().get()); + assertThat(rev.uploader._accountId).isEqualTo(admin.getId().get()); assertThat(rev.ref).isEqualTo(r.getPatchSetId().toRefName()); assertThat(rev.actions).isNotEmpty(); } @@ -321,7 +332,8 @@ @Test public void queryChangesOwnerWithDifferentUsers() throws Exception { PushOneCommit.Result r = createChange(); - assertThat(Iterables.getOnlyElement(query("owner:self")).changeId) + assertThat(Iterables.getOnlyElement( + query("project:{" + project.get() + "} owner:self")).changeId) .isEqualTo(r.getChangeId()); setApiUser(user); assertThat(query("owner:self")).isEmpty(); @@ -375,4 +387,86 @@ .get(EnumSet.of(ListChangesOption.CHECK)) .problems).isEmpty(); } + + @Test + public void commitFooters() throws Exception { + LabelType verified = category("Verified", + value(1, "Failed"), value(0, "No score"), value(-1, "Passes")); + LabelType custom1 = category("Custom1", + value(1, "Positive"), value(0, "No score"), value(-1, "Negative")); + LabelType custom2 = category("Custom2", + value(1, "Positive"), value(0, "No score"), value(-1, "Negative")); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + cfg.getLabelSections().put(verified.getName(), verified); + cfg.getLabelSections().put(custom1.getName(), verified); + cfg.getLabelSections().put(custom2.getName(), verified); + String heads = "refs/heads/*"; + AccountGroup.UUID anon = + SystemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID(); + Util.allow(cfg, Permission.forLabel("Verified"), -1, 1, anon, heads); + Util.allow(cfg, Permission.forLabel("Custom1"), -1, 1, anon, heads); + Util.allow(cfg, Permission.forLabel("Custom2"), -1, 1, anon, heads); + saveProjectConfig(project, cfg); + + PushOneCommit.Result r1 = createChange(); + r1.assertOkStatus(); + PushOneCommit.Result r2 = pushFactory.create( + db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", + r1.getChangeId()) + .to("refs/for/master"); + r2.assertOkStatus(); + + ReviewInput in = new ReviewInput(); + in.label("Code-Review", 1); + in.label("Verified", 1); + in.label("Custom1", -1); + in.label("Custom2", 1); + gApi.changes().id(r2.getChangeId()).current().review(in); + + EnumSet<ListChangesOption> options = EnumSet.of( + ListChangesOption.ALL_REVISIONS, ListChangesOption.COMMIT_FOOTERS); + ChangeInfo actual = gApi.changes().id(r2.getChangeId()).get(options); + assertThat(actual.revisions).hasSize(2); + + // No footers except on latest patch set. + assertThat(actual.revisions.get(r1.getCommit().getName()).commitWithFooters) + .isNull(); + + String expected = SUBJECT + "\n" + + "\n" + + "Change-Id: " + r2.getChangeId() + "\n" + + "Reviewed-on: " + + canonicalWebUrl.get() + r2.getChange().getId() + "\n" + + "Reviewed-by: Administrator <admin@example.com>\n" + + "Custom2: Administrator <admin@example.com>\n" + + "Tested-by: Administrator <admin@example.com>\n"; + assertThat(actual.revisions.get(r2.getCommit().getName()).commitWithFooters) + .isEqualTo(expected); + } + + @Test + public void defaultSearchDoesNotTouchDatabase() throws Exception { + PushOneCommit.Result r1 = createChange(); + gApi.changes() + .id(r1.getChangeId()) + .revision(r1.getCommit().name()) + .review(ReviewInput.approve()); + gApi.changes() + .id(r1.getChangeId()) + .revision(r1.getCommit().name()) + .submit(); + + createChange(); + + setApiUserAnonymous(); // Identified user may async get stars from DB. + atrScope.disableDb(); + assertThat(gApi.changes().query() + .withQuery( + "project:{" + project.get() + "} (status:open OR status:closed)") + // Options should match defaults in ChangeTable. + .withOption(ListChangesOption.LABELS) + .withOption(ListChangesOption.DETAILED_ACCOUNTS) + .get()) + .hasSize(2); + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/BUCK similarity index 100% rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/BUCK rename to gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/BUCK
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupAssert.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupAssert.java similarity index 94% rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupAssert.java rename to gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupAssert.java index 97503f4..c3c2224 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupAssert.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupAssert.java
@@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.acceptance.rest.group; +package com.google.gerrit.acceptance.api.group; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assert_; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import java.util.Set;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java new file mode 100644 index 0000000..8ed4165 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -0,0 +1,497 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.api.group; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.acceptance.api.group.GroupAssert.assertGroupInfo; +import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfos; + +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.extensions.api.groups.GroupInput; +import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.common.GroupOptionsInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.extensions.restapi.Url; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.server.group.SystemGroupBackend; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@NoHttpd +public class GroupsIT extends AbstractDaemonTest { + @Test + public void addToNonExistingGroup_NotFound() throws Exception { + try { + gApi.groups().id("non-existing").addMembers("admin"); + } catch (ResourceNotFoundException expected) { + // Expected. + } + } + + @Test + public void removeFromNonExistingGroup_NotFound() throws Exception { + try { + gApi.groups().id("non-existing").removeMembers("admin"); + } catch (ResourceNotFoundException expected) { + // Expected. + } + } + + @Test + public void addRemoveMember() throws Exception { + String g = createGroup("users"); + gApi.groups().id(g).addMembers("user"); + assertMembers(g, user); + + gApi.groups().id(g).removeMembers("user"); + assertNoMembers(g); + } + + @Test + public void addExistingMember_OK() throws Exception { + String g = "Administrators"; + assertMembers(g, admin); + gApi.groups().id("Administrators").addMembers("admin"); + assertMembers(g, admin); + } + + @Test + public void addMultipleMembers() throws Exception { + String g = createGroup("users"); + TestAccount u1 = accounts.create("u1", "u1@example.com", "Full Name 1"); + TestAccount u2 = accounts.create("u2", "u2@example.com", "Full Name 2"); + gApi.groups().id(g).addMembers(u1.username, u2.username); + assertMembers(g, u1, u2); + } + + @Test + public void includeRemoveGroup() throws Exception { + String p = createGroup("parent"); + String g = createGroup("newGroup"); + gApi.groups().id(p).addGroups(g); + assertIncludes(p, g); + + gApi.groups().id(p).removeGroups(g); + assertNoIncludes(p); + } + + @Test + public void includeExistingGroup_OK() throws Exception { + String p = createGroup("parent"); + String g = createGroup("newGroup"); + gApi.groups().id(p).addGroups(g); + assertIncludes(p, g); + gApi.groups().id(p).addGroups(g); + assertIncludes(p, g); + } + + @Test + public void addMultipleIncludes() throws Exception { + String p = createGroup("parent"); + String g1 = createGroup("newGroup1"); + String g2 = createGroup("newGroup2"); + List<String> groups = Lists.newLinkedList(); + groups.add(g1); + groups.add(g2); + gApi.groups().id(p).addGroups(g1, g2); + assertIncludes(p, g1, g2); + } + + @Test + public void testCreateGroup() throws Exception { + String newGroupName = name("newGroup"); + GroupInfo g = gApi.groups().create(newGroupName).get(); + assertGroupInfo(getFromCache(newGroupName), g); + } + + @Test + public void testCreateGroupWithProperties() throws Exception { + GroupInput in = new GroupInput(); + in.name = name("newGroup"); + in.description = "Test description"; + in.visibleToAll = true; + in.ownerId = getFromCache("Administrators").getGroupUUID().get(); + GroupInfo g = gApi.groups().create(in).detail(); + assertThat(g.description).isEqualTo(in.description); + assertThat(g.options.visibleToAll).isEqualTo(in.visibleToAll); + assertThat(g.ownerId).isEqualTo(in.ownerId); + } + + @Test + public void testCreateGroupWithoutCapability_Forbidden() throws Exception { + setApiUser(user); + try { + gApi.groups().create(name("newGroup")); + } catch (AuthException expected) { + // Expected. + } + } + + @Test + public void testCreateGroupWhenGroupAlreadyExists_Conflict() + throws Exception { + try { + gApi.groups().create("Administrators"); + } catch (ResourceConflictException expected) { + // Expected. + } + } + @Test + public void testGetGroup() throws Exception { + AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators")); + testGetGroup(adminGroup.getGroupUUID().get(), adminGroup); + testGetGroup(adminGroup.getName(), adminGroup); + testGetGroup(adminGroup.getId().get(), adminGroup); + } + + private void testGetGroup(Object id, AccountGroup expectedGroup) + throws Exception { + GroupInfo group = gApi.groups().id(id.toString()).get(); + assertGroupInfo(expectedGroup, group); + } + + @Test + public void testGroupName() throws Exception { + String name = name("group"); + gApi.groups().create(name); + + // get name + assertThat(gApi.groups().id(name).name()).isEqualTo(name); + + // set name with name conflict + String other = name("other"); + gApi.groups().create(other); + try { + gApi.groups().id(name).name(other); + } catch (ResourceConflictException expected) { + // Expected. + } + + // set name to same name + gApi.groups().id(name).name(name); + assertThat(gApi.groups().id(name).name()).isEqualTo(name); + + // rename + String newName = name("newName"); + gApi.groups().id(name).name(newName); + assertThat(getFromCache(newName)).isNotNull(); + assertThat(gApi.groups().id(newName).name()).isEqualTo(newName); + + assertThat(getFromCache(name)).isNull(); + try { + gApi.groups().id(name).get(); + } catch (ResourceNotFoundException expected) { + // Expceted. + } + } + + @Test + public void testGroupDescription() throws Exception { + String name = name("group"); + gApi.groups().create(name); + + // get description + assertThat(gApi.groups().id(name).description()).isEmpty(); + + // set description + String desc = "New description for the group."; + gApi.groups().id(name).description(desc); + assertThat(gApi.groups().id(name).description()).isEqualTo(desc); + + // set description to null + gApi.groups().id(name).description(null); + assertThat(gApi.groups().id(name).description()).isEmpty(); + + // set description to empty string + gApi.groups().id(name).description(""); + assertThat(gApi.groups().id(name).description()).isEmpty(); + } + + @Test + public void testGroupOptions() throws Exception { + String name = name("group"); + gApi.groups().create(name); + + // get options + assertThat(gApi.groups().id(name).options().visibleToAll).isNull(); + + // set options + GroupOptionsInfo options = new GroupOptionsInfo(); + options.visibleToAll = true; + gApi.groups().id(name).options(options); + assertThat(gApi.groups().id(name).options().visibleToAll).isTrue(); + } + + @Test + public void testGroupOwner() throws Exception { + String name = name("group"); + GroupInfo info = gApi.groups().create(name).get(); + String adminUUID = getFromCache("Administrators").getGroupUUID().get(); + String registeredUUID = SystemGroupBackend.REGISTERED_USERS.get(); + + // get owner + assertThat(Url.decode(gApi.groups().id(name).owner().id)) + .isEqualTo(info.id); + + // set owner by name + gApi.groups().id(name).owner("Registered Users"); + assertThat(Url.decode(gApi.groups().id(name).owner().id)) + .isEqualTo(registeredUUID); + + // set owner by UUID + gApi.groups().id(name).owner(adminUUID); + assertThat(Url.decode(gApi.groups().id(name).owner().id)) + .isEqualTo(adminUUID); + + // set non existing owner + try { + gApi.groups().id(name).owner("Non-Existing Group"); + } catch (UnprocessableEntityException expected) { + // Expected. + } + } + + @Test + public void listNonExistingGroupIncludes_NotFound() throws Exception { + try { + gApi.groups().id("non-existing").includedGroups(); + } catch (ResourceNotFoundException expected) { + // Expected. + } + } + + @Test + public void listEmptyGroupIncludes() throws Exception { + String gx = createGroup("gx"); + assertThat(gApi.groups().id(gx).includedGroups()).isEmpty(); + } + + @Test + public void includeNonExistingGroup() throws Exception { + String gx = createGroup("gx"); + try { + gApi.groups().id(gx).addGroups("non-existing"); + } catch (UnprocessableEntityException expecetd) { + // Expected. + } + } + + @Test + public void listNonEmptyGroupIncludes() throws Exception { + String gx = createGroup("gx"); + String gy = createGroup("gy"); + String gz = createGroup("gz"); + gApi.groups().id(gx).addGroups(gy); + gApi.groups().id(gx).addGroups(gz); + assertIncludes(gApi.groups().id(gx).includedGroups(), gy, gz); + } + + @Test + public void listOneIncludeMember() throws Exception { + String gx = createGroup("gx"); + String gy = createGroup("gy"); + gApi.groups().id(gx).addGroups(gy); + assertIncludes(gApi.groups().id(gx).includedGroups(), gy); + } + + @Test + public void listNonExistingGroupMembers_NotFound() throws Exception { + try { + gApi.groups().id("non-existing").members(); + } catch (ResourceNotFoundException expected) { + // Expected. + } + } + + @Test + public void listEmptyGroupMembers() throws Exception { + String group = createGroup("empty"); + assertThat(gApi.groups().id(group).members()).isEmpty(); + } + + @Test + public void listNonEmptyGroupMembers() throws Exception { + String group = createGroup("group"); + String user1 = createAccount("user1", group); + String user2 = createAccount("user2", group); + assertMembers(gApi.groups().id(group).members(), user1, user2); + } + + @Test + public void listOneGroupMember() throws Exception { + String group = createGroup("group"); + String user = createAccount("user1", group); + assertMembers(gApi.groups().id(group).members(), user); + } + + @Test + public void listGroupMembersRecursively() throws Exception { + String gx = createGroup("gx"); + String ux = createAccount("ux", gx); + + String gy = createGroup("gy"); + String uy = createAccount("uy", gy); + + String gz = createGroup("gz"); + String uz = createAccount("uz", gz); + + gApi.groups().id(gx).addGroups(gy); + gApi.groups().id(gy).addGroups(gz); + assertMembers(gApi.groups().id(gx).members(), ux); + assertMembers(gApi.groups().id(gx).members(true), ux, uy, uz); + } + + @Test + public void defaultGroupsCreated() throws Exception { + Iterable<String> names = gApi.groups().list().getAsMap().keySet(); + assertThat(names).containsAllOf("Administrators", "Non-Interactive Users") + .inOrder(); + } + + @Test + public void testListAllGroups() throws Exception { + List<String> expectedGroups = FluentIterable + .from(groupCache.all()) + .transform(new Function<AccountGroup, String>() { + @Override + public String apply(AccountGroup group) { + return group.getName(); + } + }).toSortedList(Ordering.natural()); + assertThat(expectedGroups.size()).isAtLeast(2); + assertThat(gApi.groups().list().getAsMap().keySet()) + .containsExactlyElementsIn(expectedGroups).inOrder(); + } + + @Test + public void testOnlyVisibleGroupsReturned() throws Exception { + String newGroupName = name("newGroup"); + GroupInput in = new GroupInput(); + in.name = newGroupName; + in.description = "a hidden group"; + in.visibleToAll = false; + in.ownerId = getFromCache("Administrators").getGroupUUID().get(); + gApi.groups().create(in); + + setApiUser(user); + assertThat(gApi.groups().list().getAsMap()) + .doesNotContainKey(newGroupName); + + setApiUser(admin); + gApi.groups().id(newGroupName).addMembers(user.username); + + setApiUser(user); + assertThat(gApi.groups().list().getAsMap()).containsKey(newGroupName); + } + + @Test + public void testAllGroupInfoFieldsSetCorrectly() throws Exception { + AccountGroup adminGroup = getFromCache("Administrators"); + Map<String, GroupInfo> groups = + gApi.groups().list().addGroup(adminGroup.getName()).getAsMap(); + assertThat(groups).hasSize(1); + assertThat(groups).containsKey("Administrators"); + assertGroupInfo(adminGroup, Iterables.getOnlyElement(groups.values())); + } + + private void assertMembers(String group, TestAccount... expectedMembers) + throws Exception { + assertMembers( + gApi.groups().id(group).members(), + TestAccount.names(expectedMembers).toArray(String.class)); + assertAccountInfos( + Arrays.asList(expectedMembers), + gApi.groups().id(group).members()); + } + + private void assertMembers(Iterable<AccountInfo> members, + String... expectedNames) { + Iterable<String> memberNames = Iterables.transform(members, + new Function<AccountInfo, String>() { + @Override + public String apply(@Nullable AccountInfo info) { + return info.name; + } + }); + assertThat(memberNames) + .containsExactlyElementsIn(Arrays.asList(expectedNames)).inOrder(); + } + + private void assertNoMembers(String group) throws Exception { + assertThat(gApi.groups().id(group).members().isEmpty()); + } + + private void assertIncludes(String group, String... expectedNames) + throws Exception { + assertIncludes(gApi.groups().id(group).includedGroups(), expectedNames); + } + + private static void assertIncludes( + Iterable<GroupInfo> includes, String... expectedNames) { + Iterable<String> includeNames = Iterables.transform( + includes, + new Function<GroupInfo, String>() { + @Override + public String apply(@Nullable GroupInfo info) { + return info.name; + } + }); + assertThat(includeNames) + .containsExactlyElementsIn(Arrays.asList(expectedNames)).inOrder(); + } + + private void assertNoIncludes(String group) throws Exception { + assertThat(gApi.groups().id(group).includedGroups().isEmpty()); + } + + private AccountGroup getFromCache(String name) throws Exception { + return groupCache.get(new AccountGroup.NameKey(name)); + } + + private String createGroup(String name) throws Exception { + return createGroup(name, "Administrators"); + } + + private String createGroup(String name, String owner) throws Exception { + name = name(name); + GroupInput in = new GroupInput(); + in.name = name; + in.ownerId = owner; + gApi.groups().create(in); + return name; + } + + private String createAccount(String name, String group) throws Exception { + name = name(name); + accounts.create(name, group); + return name; + } +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java index 7de4712..9ae95b5 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -22,35 +22,31 @@ import com.google.gerrit.common.data.Permission; import com.google.gerrit.extensions.api.projects.BranchInput; import com.google.gerrit.extensions.api.projects.ProjectInput; -import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.api.projects.PutDescriptionInput; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.RestApiException; import org.junit.Test; -import java.util.List; - @NoHttpd public class ProjectIT extends AbstractDaemonTest { @Test public void createProjectFoo() throws Exception { - String name = "foo"; + String name = name("foo"); assertThat(name).isEqualTo( gApi.projects() - .name(name) - .create() + .create(name) .get() .name); } @Test public void createProjectFooWithGitSuffix() throws Exception { - String name = "foo"; + String name = name("foo"); assertThat(name).isEqualTo( gApi.projects() - .name(name + ".git") - .create() + .create(name + ".git") .get() .name); } @@ -58,7 +54,7 @@ @Test(expected = RestApiException.class) public void createProjectFooBar() throws Exception { ProjectInput in = new ProjectInput(); - in.name = "foo"; + in.name = name("foo"); gApi.projects() .name("bar") .create(in); @@ -67,12 +63,10 @@ @Test(expected = ResourceConflictException.class) public void createProjectDuplicate() throws Exception { ProjectInput in = new ProjectInput(); - in.name = "baz"; + in.name = name("baz"); gApi.projects() - .name("baz") .create(in); gApi.projects() - .name("baz") .create(in); } @@ -86,43 +80,19 @@ } @Test - public void listProjects() throws Exception { - List<ProjectInfo> initialProjects = gApi.projects().list().get(); - - gApi.projects().name("foo").create(); - gApi.projects().name("bar").create(); - - List<ProjectInfo> allProjects = gApi.projects().list().get(); - assertThat(allProjects).hasSize(initialProjects.size() + 2); - - List<ProjectInfo> projectsWithDescription = gApi.projects().list() - .withDescription(true) - .get(); - assertThat(projectsWithDescription.get(0).description).isNotNull(); - - List<ProjectInfo> projectsWithoutDescription = gApi.projects().list() - .withDescription(false) - .get(); - assertThat(projectsWithoutDescription.get(0).description).isNull(); - - List<ProjectInfo> noMatchingProjects = gApi.projects().list() - .withPrefix("fox") - .get(); - assertThat(noMatchingProjects).isEmpty(); - - List<ProjectInfo> matchingProject = gApi.projects().list() - .withPrefix("fo") - .get(); - assertThat(matchingProject).hasSize(1); - - List<ProjectInfo> limitOneProject = gApi.projects().list() - .withLimit(1) - .get(); - assertThat(limitOneProject).hasSize(1); - - List<ProjectInfo> startAtOneProjects = gApi.projects().list() - .withStart(1) - .get(); - assertThat(startAtOneProjects).hasSize(allProjects.size() - 1); + public void description() throws Exception { + assertThat(gApi.projects() + .name(project.get()) + .description()) + .isEmpty(); + PutDescriptionInput in = new PutDescriptionInput(); + in.description = "new project description"; + gApi.projects() + .name(project.get()) + .description(in); + assertThat(gApi.projects() + .name(project.get()) + .description()) + .isEqualTo(in.description); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java index 9cc8f9c..0d46ed7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -46,14 +46,12 @@ import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Patch; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; @@ -75,7 +73,7 @@ public void reviewTriplet() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .revision(r.getCommit().name()) .review(ReviewInput.approve()); } @@ -108,11 +106,11 @@ public void submit() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .current() .review(ReviewInput.approve()); gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .current() .submit(); } @@ -121,14 +119,14 @@ public void submitOnBehalfOf() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .current() .review(ReviewInput.approve()); SubmitInput in = new SubmitInput(); in.onBehalfOf = admin2.email; in.waitForMerge = true; gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .current() .submit(in); } @@ -153,12 +151,12 @@ .branch(in.destination) .create(new BranchInput()); ChangeApi orig = gApi.changes() - .id("p~master~" + r.getChangeId()); + .id(project.get() + "~master~" + r.getChangeId()); - assertThat((Iterable<?>)orig.get().messages).hasSize(1); + assertThat(orig.get().messages).hasSize(1); ChangeApi cherry = orig.revision(r.getCommit().name()) .cherryPick(in); - assertThat((Iterable<?>)orig.get().messages).hasSize(2); + assertThat(orig.get().messages).hasSize(2); String cherryPickedRevision = cherry.get().currentRevision; String expectedMessage = String.format( @@ -170,13 +168,33 @@ origIt.next(); assertThat(origIt.next().message).isEqualTo(expectedMessage); - assertThat((Iterable<?>)cherry.get().messages).hasSize(1); + assertThat(cherry.get().messages).hasSize(1); Iterator<ChangeMessageInfo> cherryIt = cherry.get().messages.iterator(); expectedMessage = "Patch Set 1: Cherry Picked from branch master."; assertThat(cherryIt.next().message).isEqualTo(expectedMessage); assertThat(cherry.get().subject).contains(in.message); - assertThat(cherry.get().topic).isEqualTo("someTopic"); + assertThat(cherry.get().topic).isEqualTo("someTopic-foo"); + cherry.current().review(ReviewInput.approve()); + cherry.current().submit(); + } + + @Test + public void cherryPickwithNoTopic() throws Exception { + PushOneCommit.Result r = pushTo("refs/for/master"); + CherryPickInput in = new CherryPickInput(); + in.destination = "foo"; + in.message = "it goes to stable branch"; + gApi.projects() + .name(project.get()) + .branch(in.destination) + .create(new BranchInput()); + ChangeApi orig = gApi.changes() + .id(project.get() + "~master~" + r.getChangeId()); + + ChangeApi cherry = orig.revision(r.getCommit().name()) + .cherryPick(in); + assertThat(cherry.get().topic).isNull(); cherry.current().review(ReviewInput.approve()); cherry.current().submit(); } @@ -188,11 +206,11 @@ in.destination = "master"; in.message = "it generates a new patch set\n\nChange-Id: " + r.getChangeId(); ChangeInfo cherryInfo = gApi.changes() - .id("p~master~" + r.getChangeId()) + .id(project.get() + "~master~" + r.getChangeId()) .revision(r.getCommit().name()) .cherryPick(in) .get(); - assertThat((Iterable<?>)cherryInfo.messages).hasSize(2); + assertThat(cherryInfo.messages).hasSize(2); Iterator<ChangeMessageInfo> cherryIt = cherryInfo.messages.iterator(); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 1."); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 2."); @@ -202,8 +220,8 @@ public void cherryPickToSameBranchWithRebase() throws Exception { // Push a new change, then merge it PushOneCommit.Result baseChange = createChange(); - RevisionApi baseRevision = - gApi.changes().id("p~master~" + baseChange.getChangeId()).current(); + String triplet = project.get() + "~master~" + baseChange.getChangeId(); + RevisionApi baseRevision = gApi.changes().id(triplet).current(); baseRevision.review(ReviewInput.approve()); baseRevision.submit(); @@ -214,22 +232,23 @@ String subject = "Test change\n\n" + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; PushOneCommit push = - pushFactory.create(db, admin.getIdent(), subject, + pushFactory.create(db, admin.getIdent(), testRepo, subject, "another_file.txt", "another content"); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push.to("refs/for/master"); // Change 2's parent should be change 1 assertThat(r2.getCommit().getParents()[0].name()) .isEqualTo(r1.getCommit().name()); // Cherry pick change 2 onto the same branch - ChangeApi orig = gApi.changes().id("p~master~" + r2.getChangeId()); + triplet = project.get() + "~master~" + r2.getChangeId(); + ChangeApi orig = gApi.changes().id(triplet); CherryPickInput in = new CherryPickInput(); in.destination = "master"; in.message = subject; ChangeApi cherry = orig.revision(r2.getCommit().name()).cherryPick(in); ChangeInfo cherryInfo = cherry.get(); - assertThat((Iterable<?>)cherryInfo.messages).hasSize(2); + assertThat(cherryInfo.messages).hasSize(2); Iterator<ChangeMessageInfo> cherryIt = cherryInfo.messages.iterator(); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 1."); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 2."); @@ -252,12 +271,12 @@ .branch(in.destination) .create(new BranchInput()); ChangeApi orig = gApi.changes() - .id("p~master~" + r.getChangeId()); + .id(project.get() + "~master~" + r.getChangeId()); - assertThat((Iterable<?>)orig.get().messages).hasSize(1); + assertThat(orig.get().messages).hasSize(1); ChangeApi cherry = orig.revision(r.getCommit().name()) .cherryPick(in); - assertThat((Iterable<?>)orig.get().messages).hasSize(2); + assertThat(orig.get().messages).hasSize(2); assertThat(cherry.get().subject).contains(in.message); cherry.current().review(ReviewInput.approve()); @@ -283,12 +302,13 @@ .create(new BranchInput()); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "another content"); - push.to(git, "refs/heads/foo"); + push.to("refs/heads/foo"); - ChangeApi orig = gApi.changes().id("p~master~" + r.getChangeId()); - assertThat((Iterable<?>)orig.get().messages).hasSize(1); + String triplet = project.get() + "~master~" + r.getChangeId(); + ChangeApi orig = gApi.changes().id(triplet); + assertThat(orig.get().messages).hasSize(1); try { orig.revision(r.getCommit().name()).cherryPick(in); @@ -300,12 +320,12 @@ @Test public void canRebase() throws Exception { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r1 = push.to(git, "refs/for/master"); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r1 = push.to("refs/for/master"); merge(r1); - push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r2 = push.to("refs/for/master"); boolean canRebase = gApi.changes() .id(r2.getChangeId()) .revision(r2.getCommit().name()) @@ -313,9 +333,9 @@ assertThat(canRebase).isFalse(); merge(r2); - git.checkout().setName(r1.getCommit().name()).call(); - push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r3 = push.to(git, "refs/for/master"); + testRepo.reset(r1.getCommit()); + push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r3 = push.to("refs/for/master"); canRebase = gApi.changes() .id(r3.getChangeId()) @@ -326,8 +346,8 @@ @Test public void setUnsetReviewedFlag() throws Exception { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r = push.to("refs/for/master"); gApi.changes() .id(r.getChangeId()) @@ -345,31 +365,31 @@ .current() .setReviewed(PushOneCommit.FILE_NAME, false); - assertThat((Iterable<?>)gApi.changes().id(r.getChangeId()).current().reviewed()) + assertThat(gApi.changes().id(r.getChangeId()).current().reviewed()) .isEmpty(); } @Test public void mergeable() throws Exception { - ObjectId initial = git.getRepository().getRef(HEAD).getLeaf().getObjectId(); + ObjectId initial = repo().getRef(HEAD).getLeaf().getObjectId(); PushOneCommit push1 = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "push 1 content"); - PushOneCommit.Result r1 = push1.to(git, "refs/for/master"); + PushOneCommit.Result r1 = push1.to("refs/for/master"); assertMergeable(r1.getChangeId(), true); merge(r1); // Reset HEAD to initial so the new change is a merge conflict. - RefUpdate ru = git.getRepository().updateRef(HEAD); + RefUpdate ru = repo().updateRef(HEAD); ru.setNewObjectId(initial); assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED); PushOneCommit push2 = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "push 2 content"); - PushOneCommit.Result r2 = push2.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push2.to("refs/for/master"); assertMergeable(r2.getChangeId(), false); // TODO(dborowitz): Test for other-branches. } @@ -505,6 +525,19 @@ CommentInfo comment = Iterables.getOnlyElement(out.get(FILE_NAME)); assertThat(comment.message).isEqualTo(in.message); assertThat(comment.author.email).isEqualTo(admin.email); + assertThat(comment.path).isNull(); + + List<CommentInfo> list = gApi.changes() + .id(r.getChangeId()) + .revision(r.getCommit().name()) + .commentsAsList(); + assertThat(list).hasSize(1); + + CommentInfo comment2 = list.get(0); + assertThat(comment2.path).isEqualTo(FILE_NAME); + assertThat(comment2.line).isEqualTo(comment.line); + assertThat(comment2.message).isEqualTo(comment.message); + assertThat(comment2.author.email).isEqualTo(comment.author.email); assertThat(gApi.changes() .id(r.getChangeId()) @@ -521,15 +554,14 @@ } private PushOneCommit.Result updateChange(PushOneCommit.Result r, - String content) throws GitAPIException, IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + String content) throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId()); - return push.to(git, "refs/for/master"); + return push.to("refs/for/master"); } - private PushOneCommit.Result createDraft() throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - return push.to(git, "refs/drafts/master"); + private PushOneCommit.Result createDraft() throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + return push.to("refs/drafts/master"); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java index 4726079..82f8c59 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -16,8 +16,6 @@ import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; -import static com.google.gerrit.acceptance.GitUtil.createProject; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; @@ -34,6 +32,8 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.acceptance.RestSession; +import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.common.data.LabelType; import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.common.ApprovalInfo; @@ -54,12 +54,12 @@ import com.google.gerrit.server.edit.UnchangedCommitMessageException; import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.project.InvalidChangeOperationException; +import com.google.gerrit.server.project.Util; import com.google.gson.stream.JsonReader; import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Inject; import org.apache.commons.codec.binary.StringUtils; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RefUpdate; @@ -109,12 +109,12 @@ @Before public void setUp() throws Exception { db = reviewDbProvider.open(); - changeId = newChange(git, admin.getIdent()); + changeId = newChange(admin.getIdent()); ps = getCurrentPatchSet(changeId); - amendChange(git, admin.getIdent(), changeId); + amendChange(admin.getIdent(), changeId); change = getChange(changeId); assertThat(ps).isNotNull(); - changeId2 = newChange2(git, admin.getIdent()); + changeId2 = newChange2(admin.getIdent()); change2 = getChange(changeId2); assertThat(change2).isNotNull(); ps2 = getCurrentPatchSet(changeId2); @@ -245,9 +245,9 @@ assertThat(edit.getBasePatchSet().getPatchSetId()).isEqualTo( current.getPatchSetId()); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, FILE_NAME, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_NEW2), changeId2); - push.to(git, "refs/for/master").assertOkStatus(); + push.to("refs/for/master").assertOkStatus(); RestResponse r = adminSession.post(urlRebase()); assertThat(r.getStatusCode()).isEqualTo(SC_CONFLICT); } @@ -267,10 +267,11 @@ } @Test + @TestProjectInput(createEmptyCommit = false) public void updateRootCommitMessage() throws Exception { - createProject(sshSession, "root-msg-test", null, false); - git = cloneProject(sshSession.getUrl() + "/root-msg-test"); - changeId = newChange(git, admin.getIdent()); + // Re-clone empty repo; TestRepository doesn't let us reset to unborn head. + testRepo = cloneProject(project); + changeId = newChange(admin.getIdent()); change = getChange(changeId); assertThat(modifier.createEdit(change, getCurrentPatchSet(changeId))) @@ -455,6 +456,30 @@ } @Test + public void revertChanges() throws Exception { + assertThat(modifier.createEdit(change2, ps2)).isEqualTo( + RefUpdate.Result.NEW); + Optional<ChangeEdit> edit = editUtil.byChange(change2); + assertThat(modifier.restoreFile(edit.get(), FILE_NAME)).isEqualTo( + RefUpdate.Result.FORCED); + edit = editUtil.byChange(change2); + assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()), + ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_OLD); + assertThat( + modifier.modifyFile(editUtil.byChange(change2).get(), FILE_NAME, + RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED); + edit = editUtil.byChange(change2); + assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()), + ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_NEW); + assertThat(modifier.restoreFile(edit.get(), FILE_NAME)).isEqualTo( + RefUpdate.Result.FORCED); + edit = editUtil.byChange(change2); + assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()), + ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_OLD); + editUtil.delete(edit.get()); + } + + @Test public void renameFileRest() throws Exception { assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW); Post.Input in = new Post.Input(); @@ -624,10 +649,11 @@ @Test public void editCommitMessageCopiesLabelScores() throws Exception { String cr = "Code-Review"; - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - cfg.getLabelSections().get(cr) - .setCopyAllScoresIfNoCodeChange(true); - saveProjectConfig(allProjects, cfg); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + LabelType codeReview = Util.codeReview(); + codeReview.setCopyAllScoresIfNoCodeChange(true); + cfg.getLabelSections().put(cr, codeReview); + saveProjectConfig(project, cfg); String changeId = change.getKey().get(); ReviewInput r = new ReviewInput(); @@ -654,25 +680,25 @@ assertThat(approvals.get(0).value).isEqualTo(1); } - private String newChange(Git git, PersonIdent ident) throws Exception { + private String newChange(PersonIdent ident) throws Exception { PushOneCommit push = - pushFactory.create(db, ident, PushOneCommit.SUBJECT, FILE_NAME, + pushFactory.create(db, ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, StandardCharsets.UTF_8)); - return push.to(git, "refs/for/master").getChangeId(); + return push.to("refs/for/master").getChangeId(); } - private String amendChange(Git git, PersonIdent ident, String changeId) throws Exception { + private String amendChange(PersonIdent ident, String changeId) throws Exception { PushOneCommit push = - pushFactory.create(db, ident, PushOneCommit.SUBJECT, FILE_NAME2, + pushFactory.create(db, ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME2, new String(CONTENT_NEW2, StandardCharsets.UTF_8), changeId); - return push.to(git, "refs/for/master").getChangeId(); + return push.to("refs/for/master").getChangeId(); } - private String newChange2(Git git, PersonIdent ident) throws Exception { + private String newChange2(PersonIdent ident) throws Exception { PushOneCommit push = - pushFactory.create(db, ident, PushOneCommit.SUBJECT, FILE_NAME, + pushFactory.create(db, ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, StandardCharsets.UTF_8)); - return push.rm(git, "refs/for/master").getChangeId(); + return push.rm("refs/for/master").getChangeId(); } private Change getChange(String changeId) throws Exception {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java index e65d51e..2323270 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -16,30 +16,24 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; import com.google.common.collect.ImmutableSet; import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.LabelInfo; -import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.testutil.ConfigSuite; -import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; -import com.jcraft.jsch.JSchException; - -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Config; import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.util.Set; public abstract class AbstractPushForReview extends AbstractDaemonTest { @@ -52,6 +46,7 @@ private NotesMigration notesMigration; protected enum Protocol { + // TODO(dborowitz): TEST. SSH, HTTP } @@ -62,7 +57,7 @@ sshUrl = sshSession.getUrl(); } - protected void selectProtocol(Protocol p) throws GitAPIException, IOException { + protected void selectProtocol(Protocol p) throws Exception { String url; switch (p) { case SSH: @@ -74,20 +69,18 @@ default: throw new IllegalArgumentException("unexpected protocol: " + p); } - git = cloneProject(url + "/" + project.get()); + testRepo = GitUtil.cloneProject(project, url + "/" + project.get()); } @Test - public void testPushForMaster() throws GitAPIException, OrmException, - IOException { + public void testPushForMaster() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master"); r.assertOkStatus(); r.assertChange(Change.Status.NEW, null); } @Test - public void testPushForMasterWithTopic() throws GitAPIException, - OrmException, IOException { + public void testPushForMasterWithTopic() throws Exception { // specify topic in ref String topic = "my/topic"; PushOneCommit.Result r = pushTo("refs/for/master/" + topic); @@ -101,8 +94,7 @@ } @Test - public void testPushForMasterWithCc() throws GitAPIException, OrmException, - IOException, JSchException { + public void testPushForMasterWithCc() throws Exception { // cc one user String topic = "my/topic"; PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email); @@ -125,8 +117,7 @@ } @Test - public void testPushForMasterWithReviewer() throws GitAPIException, - OrmException, IOException, JSchException { + public void testPushForMasterWithReviewer() throws Exception { // add one reviewer String topic = "my/topic"; PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%r=" + user.email); @@ -150,8 +141,7 @@ } @Test - public void testPushForMasterAsDraft() throws GitAPIException, OrmException, - IOException { + public void testPushForMasterAsDraft() throws Exception { // create draft by pushing to 'refs/drafts/' PushOneCommit.Result r = pushTo("refs/drafts/master"); r.assertOkStatus(); @@ -164,8 +154,7 @@ } @Test - public void testPushForMasterAsEdit() throws GitAPIException, - IOException, RestApiException { + public void testPushForMasterAsEdit() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master"); r.assertOkStatus(); EditInfo edit = getEdit(r.getChangeId()); @@ -179,65 +168,59 @@ } @Test - public void testPushForMasterWithApprovals() throws GitAPIException, - IOException, RestApiException { + public void testPushForMasterWithApprovals() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master/%l=Code-Review"); r.assertOkStatus(); ChangeInfo ci = get(r.getChangeId()); LabelInfo cr = ci.labels.get("Code-Review"); assertThat(cr.all).hasSize(1); assertThat(cr.all.get(0).name).isEqualTo("Administrator"); - assertThat(cr.all.get(0).value.intValue()).is(1); + assertThat(cr.all.get(0).value).isEqualTo(1); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId()); - r = push.to(git, "refs/for/master/%l=Code-Review+2"); + r = push.to("refs/for/master/%l=Code-Review+2"); ci = get(r.getChangeId()); cr = ci.labels.get("Code-Review"); assertThat(cr.all).hasSize(1); assertThat(cr.all.get(0).name).isEqualTo("Administrator"); - assertThat(cr.all.get(0).value.intValue()).is(2); + assertThat(cr.all.get(0).value).isEqualTo(2); } @Test - public void testPushNewPatchsetToRefsChanges() throws GitAPIException, - IOException, OrmException { + public void testPushNewPatchsetToRefsChanges() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master"); r.assertOkStatus(); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId()); - r = push.to(git, "refs/changes/" + r.getChange().change().getId().get()); + r = push.to("refs/changes/" + r.getChange().change().getId().get()); r.assertOkStatus(); } @Test - public void testPushForMasterWithApprovals_MissingLabel() throws GitAPIException, - IOException { + public void testPushForMasterWithApprovals_MissingLabel() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master/%l=Verify"); r.assertErrorStatus("label \"Verify\" is not a configured label"); } @Test - public void testPushForMasterWithApprovals_ValueOutOfRange() throws GitAPIException, - IOException { + public void testPushForMasterWithApprovals_ValueOutOfRange() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master/%l=Code-Review-3"); r.assertErrorStatus("label \"Code-Review\": -3 is not a valid value"); } @Test - public void testPushForNonExistingBranch() throws GitAPIException, - IOException { + public void testPushForNonExistingBranch() throws Exception { String branchName = "non-existing"; PushOneCommit.Result r = pushTo("refs/for/" + branchName); r.assertErrorStatus("branch " + branchName + " not found"); } @Test - public void testPushForMasterWithHashtags() throws GitAPIException, - OrmException, IOException, RestApiException { + public void testPushForMasterWithHashtags() throws Exception { // Hashtags currently only work when noteDB is enabled assume().that(notesMigration.enabled()).isTrue(); @@ -250,23 +233,22 @@ r.assertChange(Change.Status.NEW, null); Set<String> hashtags = gApi.changes().id(r.getChangeId()).getHashtags(); - assertThat((Iterable<?>)hashtags).containsExactlyElementsIn(expected); + assertThat(hashtags).containsExactlyElementsIn(expected); // specify a single hashtag as option in new patch set String hashtag2 = "tag2"; PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId()); - r = push.to(git, "refs/for/master/%hashtag=" + hashtag2); + r = push.to("refs/for/master/%hashtag=" + hashtag2); r.assertOkStatus(); expected = ImmutableSet.of(hashtag1, hashtag2); hashtags = gApi.changes().id(r.getChangeId()).getHashtags(); - assertThat((Iterable<?>)hashtags).containsExactlyElementsIn(expected); + assertThat(hashtags).containsExactlyElementsIn(expected); } @Test - public void testPushForMasterWithMultipleHashtags() throws GitAPIException, - OrmException, IOException, RestApiException { + public void testPushForMasterWithMultipleHashtags() throws Exception { // Hashtags currently only work when noteDB is enabled assume().that(notesMigration.enabled()).isTrue(); @@ -281,25 +263,23 @@ r.assertChange(Change.Status.NEW, null); Set<String> hashtags = gApi.changes().id(r.getChangeId()).getHashtags(); - assertThat((Iterable<?>)hashtags).containsExactlyElementsIn(expected); + assertThat(hashtags).containsExactlyElementsIn(expected); // specify multiple hashtags as options in new patch set String hashtag3 = "tag3"; String hashtag4 = "tag4"; PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId()); - r = push.to(git, - "refs/for/master%hashtag=" + hashtag3 + ",hashtag=" + hashtag4); + r = push.to("refs/for/master%hashtag=" + hashtag3 + ",hashtag=" + hashtag4); r.assertOkStatus(); expected = ImmutableSet.of(hashtag1, hashtag2, hashtag3, hashtag4); hashtags = gApi.changes().id(r.getChangeId()).getHashtags(); - assertThat((Iterable<?>)hashtags).containsExactlyElementsIn(expected); + assertThat(hashtags).containsExactlyElementsIn(expected); } @Test - public void testPushForMasterWithHashtagsNoteDbDisabled() throws GitAPIException, - IOException { + public void testPushForMasterWithHashtagsNoteDbDisabled() throws Exception { // push with hashtags should fail when noteDb is disabled assume().that(notesMigration.enabled()).isFalse(); PushOneCommit.Result r = pushTo("refs/for/master%hashtag=tag1");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java new file mode 100644 index 0000000..8bf4511 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
@@ -0,0 +1,82 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.git; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.reviewdb.client.Project; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.RefSpec; + +public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest { + protected TestRepository<?> createProjectWithPush(String name) + throws Exception { + Project.NameKey project = createProject(name); + grant(Permission.PUSH, project, "refs/heads/*"); + grant(Permission.SUBMIT, project, "refs/for/refs/heads/*"); + return cloneProject(project); + } + + protected void createSubscription( + TestRepository<?> repo, String branch, String subscribeToRepo, + String subscribeToBranch) throws Exception { + subscribeToRepo = name(subscribeToRepo); + + // The submodule subscription module checks for gerrit.canonicalWebUrl to + // detect if it's configured for automatic updates. It doesn't matter if + // it serves from that URL. + String url = cfg.getString("gerrit", null, "canonicalWebUrl") + "/" + + subscribeToRepo; + + Config cfg = new Config(); + cfg.setString("submodule", subscribeToRepo, "path", subscribeToRepo); + cfg.setString("submodule", subscribeToRepo, "url", url); + cfg.setString("submodule", subscribeToRepo, "branch", subscribeToBranch); + + repo.branch("HEAD").commit().insertChangeId() + .message("subject: adding new subscription") + .add(".gitmodules", cfg.toText().toString()) + .create(); + + repo.git().push().setRemote("origin").setRefSpecs( + new RefSpec("HEAD:refs/heads/" + branch)).call(); + } + + protected void expectToHaveSubmoduleState(TestRepository<?> repo, + String branch, String submodule, ObjectId expectedId) throws Exception { + + submodule = name(submodule); + ObjectId commitId = repo.git().fetch().setRemote("origin").call() + .getAdvertisedRef("refs/heads/" + branch).getObjectId(); + + RevWalk rw = repo.getRevWalk(); + RevCommit c = rw.parseCommit(commitId); + rw.parseBody(c.getTree()); + + RevTree tree = c.getTree(); + RevObject actualId = repo.get(tree, submodule); + + assertThat(actualId).isEqualTo(expectedId); + } +} \ No newline at end of file
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUCK index f36c447..446a183 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUCK +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUCK
@@ -5,8 +5,11 @@ 'DraftChangeBlockedIT.java', 'ForcePushIT.java', 'SubmitOnPushIT.java', + 'SubmoduleSubscriptionsWholeTopicMergeIT.java', + 'SubmoduleSubscriptionsIT.java', 'VisibleRefFilterIT.java', ], + deps = [':submodule_util'], labels = ['git'], ) @@ -21,3 +24,9 @@ srcs = ['AbstractPushForReview.java'], deps = ['//gerrit-acceptance-tests:lib'], ) + +java_library( + name = 'submodule_util', + srcs = ['AbstractSubmoduleSubscription.java',], + deps = ['//gerrit-acceptance-tests:lib',] +)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/DraftChangeBlockedIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/DraftChangeBlockedIT.java index 20a698d..27b8b0a 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/DraftChangeBlockedIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/DraftChangeBlockedIT.java
@@ -21,23 +21,19 @@ import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.common.data.Permission; -import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; import org.junit.Before; import org.junit.Test; -import java.io.IOException; - @NoHttpd public class DraftChangeBlockedIT extends AbstractDaemonTest { @Before public void setUp() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); block(cfg, Permission.PUSH, ANONYMOUS_USERS, "refs/drafts/*"); - saveProjectConfig(cfg); - projectCache.evict(cfg.getProject()); + saveProjectConfig(project, cfg); } @Test @@ -53,13 +49,4 @@ PushOneCommit.Result r = pushTo("refs/for/master%draft"); r.assertErrorStatus("cannot upload drafts"); } - - private void saveProjectConfig(ProjectConfig cfg) throws IOException { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); - try { - cfg.commit(md); - } finally { - md.close(); - } - } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ForcePushIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ForcePushIT.java index 5da524e..26db819 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ForcePushIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/ForcePushIT.java
@@ -29,42 +29,42 @@ @Test public void forcePushNotAllowed() throws Exception { - ObjectId initial = git.getRepository().getRef(HEAD).getLeaf().getObjectId(); + ObjectId initial = repo().getRef(HEAD).getLeaf().getObjectId(); PushOneCommit push1 = - pushFactory.create(db, admin.getIdent(), "change1", "a.txt", "content"); - PushOneCommit.Result r1 = push1.to(git, "refs/heads/master"); + pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content"); + PushOneCommit.Result r1 = push1.to("refs/heads/master"); r1.assertOkStatus(); // Reset HEAD to initial so the new change is a non-fast forward - RefUpdate ru = git.getRepository().updateRef(HEAD); + RefUpdate ru = repo().updateRef(HEAD); ru.setNewObjectId(initial); assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED); PushOneCommit push2 = - pushFactory.create(db, admin.getIdent(), "change2", "b.txt", "content"); + pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content"); push2.setForce(true); - PushOneCommit.Result r2 = push2.to(git, "refs/heads/master"); + PushOneCommit.Result r2 = push2.to("refs/heads/master"); r2.assertErrorStatus("non-fast forward"); } @Test public void forcePushAllowed() throws Exception { - ObjectId initial = git.getRepository().getRef(HEAD).getLeaf().getObjectId(); + ObjectId initial = repo().getRef(HEAD).getLeaf().getObjectId(); grant(Permission.PUSH, project, "refs/*", true); PushOneCommit push1 = - pushFactory.create(db, admin.getIdent(), "change1", "a.txt", "content"); - PushOneCommit.Result r1 = push1.to(git, "refs/heads/master"); + pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content"); + PushOneCommit.Result r1 = push1.to("refs/heads/master"); r1.assertOkStatus(); // Reset HEAD to initial so the new change is a non-fast forward - RefUpdate ru = git.getRepository().updateRef(HEAD); + RefUpdate ru = repo().updateRef(HEAD); ru.setNewObjectId(initial); assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED); PushOneCommit push2 = - pushFactory.create(db, admin.getIdent(), "change2", "b.txt", "content"); + pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content"); push2.setForce(true); - PushOneCommit.Result r2 = push2.to(git, "refs/heads/master"); + PushOneCommit.Result r2 = push2.to("refs/heads/master"); r2.assertOkStatus(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmitOnPushIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmitOnPushIT.java index cf0945e..5066e66 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmitOnPushIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
@@ -32,7 +32,6 @@ import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; @@ -55,7 +54,8 @@ private ChangeNotes.Factory changeNotesFactory; @Inject - private @GerritPersonIdent PersonIdent serverIdent; + @GerritPersonIdent + private PersonIdent serverIdent; @Test public void submitOnPush() throws Exception { @@ -73,9 +73,9 @@ grant(Permission.CREATE, project, "refs/tags/*"); grant(Permission.PUSH, project, "refs/tags/*"); PushOneCommit.Tag tag = new PushOneCommit.Tag("v1.0"); - PushOneCommit push = pushFactory.create(db, admin.getIdent()); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); push.setTag(tag); - PushOneCommit.Result r = push.to(git, "refs/for/master%submit"); + PushOneCommit.Result r = push.to("refs/for/master%submit"); r.assertOkStatus(); r.assertChange(Change.Status.MERGED, null, admin); assertSubmitApproval(r.getPatchSetId()); @@ -90,9 +90,9 @@ grant(Permission.PUSH, project, "refs/tags/*"); PushOneCommit.AnnotatedTag tag = new PushOneCommit.AnnotatedTag("v1.0", "annotation", admin.getIdent()); - PushOneCommit push = pushFactory.create(db, admin.getIdent()); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); push.setTag(tag); - PushOneCommit.Result r = push.to(git, "refs/for/master%submit"); + PushOneCommit.Result r = push.to("refs/for/master%submit"); r.assertOkStatus(); r.assertChange(Change.Status.MERGED, null, admin); assertSubmitApproval(r.getPatchSetId()); @@ -104,9 +104,8 @@ public void submitOnPushToRefsMetaConfig() throws Exception { grant(Permission.SUBMIT, project, "refs/for/refs/meta/config"); - git.fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call(); - ObjectId objectId = git.getRepository().getRef("refs/meta/config").getObjectId(); - git.checkout().setName(objectId.getName()).call(); + git().fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call(); + testRepo.reset("refs/meta/config"); PushOneCommit.Result r = pushTo("refs/for/refs/meta/config%submit"); r.assertOkStatus(); @@ -117,10 +116,9 @@ @Test public void submitOnPushMergeConflict() throws Exception { - String master = "refs/heads/master"; - ObjectId objectId = git.getRepository().getRef(master).getObjectId(); - push(master, "one change", "a.txt", "some content"); - git.checkout().setName(objectId.getName()).call(); + ObjectId objectId = repo().getRef("HEAD").getObjectId(); + push("refs/heads/master", "one change", "a.txt", "some content"); + testRepo.reset(objectId); grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); PushOneCommit.Result r = @@ -133,9 +131,9 @@ @Test public void submitOnPushSuccessfulMerge() throws Exception { String master = "refs/heads/master"; - ObjectId objectId = git.getRepository().getRef(master).getObjectId(); + ObjectId objectId = repo().getRef("HEAD").getObjectId(); push(master, "one change", "a.txt", "some content"); - git.checkout().setName(objectId.getName()).call(); + testRepo.reset(objectId); grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); PushOneCommit.Result r = @@ -198,7 +196,7 @@ push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content"); r.assertOkStatus(); - git.push() + git().push() .setRefSpecs(new RefSpec(r.getCommitId().name() + ":refs/heads/master")) .call(); assertCommit(project, "refs/heads/master"); @@ -214,10 +212,10 @@ r.assertOkStatus(); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId()); - r = push.to(git, "refs/heads/master"); + r = push.to("refs/heads/master"); r.assertOkStatus(); assertCommit(project, "refs/heads/master"); @@ -255,7 +253,7 @@ try (Repository r = repoManager.openRepository(project); RevWalk rw = new RevWalk(r)) { RevCommit c = rw.parseCommit(r.getRef(branch).getObjectId()); - assertThat(c.getParentCount()).is(2); + assertThat(c.getParentCount()).isEqualTo(2); assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\""); assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email); assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo( @@ -290,17 +288,16 @@ } private PushOneCommit.Result push(String ref, String subject, - String fileName, String content) throws GitAPIException, IOException { + String fileName, String content) throws Exception { PushOneCommit push = - pushFactory.create(db, admin.getIdent(), subject, fileName, content); - return push.to(git, ref); + pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content); + return push.to(ref); } private PushOneCommit.Result push(String ref, String subject, - String fileName, String content, String changeId) throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent(), subject, + String fileName, String content, String changeId) throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content, changeId); - return push.to(git, ref); + return push.to(ref); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java new file mode 100644 index 0000000..3ffe07c --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
@@ -0,0 +1,201 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.git; + +import static com.google.common.truth.Truth.assertThat; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.RefSpec; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription { + + @Test + public void testSubscriptionToEmptyRepo() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + ObjectId subHEAD = pushChangeTo(subRepo, "master"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEAD); + } + + @Test + public void testSubscriptionToExistingRepo() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + pushChangeTo(subRepo, "master"); + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + ObjectId subHEAD = pushChangeTo(subRepo, "master"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEAD); + } + + @Test + public void testSubscriptionUnsubscribe() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + pushChangeTo(subRepo, "master"); + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + + pushChangeTo(subRepo, "master"); + ObjectId subHEADbeforeUnsubscribing = pushChangeTo(subRepo, "master"); + + deleteAllSubscriptions(superRepo, "master"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEADbeforeUnsubscribing); + + pushChangeTo(superRepo, "master", "commit after unsubscribe"); + pushChangeTo(subRepo, "master", "commit after unsubscribe"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEADbeforeUnsubscribing); + } + + @Test + public void testSubscriptionUnsubscribeByDeletingGitModules() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + pushChangeTo(subRepo, "master"); + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + + pushChangeTo(subRepo, "master"); + ObjectId subHEADbeforeUnsubscribing = pushChangeTo(subRepo, "master"); + + deleteGitModulesFile(superRepo, "master"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEADbeforeUnsubscribing); + + pushChangeTo(superRepo, "master", "commit after unsubscribe"); + pushChangeTo(subRepo, "master", "commit after unsubscribe"); + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEADbeforeUnsubscribing); + } + + @Test + public void testSubscriptionToDifferentBranches() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + createSubscription(superRepo, "master", "subscribed-to-project", "foo"); + ObjectId subFoo = pushChangeTo(subRepo, "foo"); + pushChangeTo(subRepo, "master"); + + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subFoo); + } + + @Test + public void testCircularSubscriptionIsDetected() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + + pushChangeTo(subRepo, "master"); + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + createSubscription(subRepo, "master", "super-project", "master"); + + ObjectId subHEAD = pushChangeTo(subRepo, "master"); + pushChangeTo(superRepo, "master"); + + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subHEAD); + + assertThat(hasSubmodule(subRepo, "master", "super-project")).isFalse(); + } + + private static AtomicInteger contentCounter = new AtomicInteger(0); + + private ObjectId pushChangeTo(TestRepository<?> repo, String branch, String message) + throws Exception { + + ObjectId ret = repo.branch("HEAD").commit().insertChangeId() + .message(message) + .add("a.txt", "a contents: " + contentCounter.addAndGet(1)) + .create(); + + repo.git().push().setRemote("origin").setRefSpecs( + new RefSpec("HEAD:refs/heads/" + branch)).call(); + + return ret; + } + + private ObjectId pushChangeTo(TestRepository<?> repo, String branch) + throws Exception { + return pushChangeTo(repo, branch, "some change"); + } + + private void deleteAllSubscriptions(TestRepository<?> repo, String branch) + throws Exception { + repo.git().fetch().setRemote("origin").call(); + repo.reset("refs/remotes/origin/" + branch); + + ObjectId expectedId = repo.branch("HEAD").commit().insertChangeId() + .message("delete contents in .gitmodules") + .add(".gitmodules", "") // Just remove the contents of the file! + .create(); + repo.git().push().setRemote("origin").setRefSpecs( + new RefSpec("HEAD:refs/heads/" + branch)).call(); + + ObjectId actualId = repo.git().fetch().setRemote("origin").call() + .getAdvertisedRef("refs/heads/master").getObjectId(); + assertThat(actualId).isEqualTo(expectedId); + } + + private void deleteGitModulesFile(TestRepository<?> repo, String branch) + throws Exception { + repo.git().fetch().setRemote("origin").call(); + repo.reset("refs/remotes/origin/" + branch); + + ObjectId expectedId = repo.branch("HEAD").commit().insertChangeId() + .message("delete .gitmodules") + .rm(".gitmodules") + .create(); + repo.git().push().setRemote("origin").setRefSpecs( + new RefSpec("HEAD:refs/heads/" + branch)).call(); + + ObjectId actualId = repo.git().fetch().setRemote("origin").call() + .getAdvertisedRef("refs/heads/master").getObjectId(); + assertThat(actualId).isEqualTo(expectedId); + } + + private boolean hasSubmodule(TestRepository<?> repo, String branch, + String submodule) throws Exception { + + ObjectId commitId = repo.git().fetch().setRemote("origin").call() + .getAdvertisedRef("refs/heads/" + branch).getObjectId(); + + RevWalk rw = repo.getRevWalk(); + RevCommit c = rw.parseCommit(commitId); + rw.parseBody(c.getTree()); + + RevTree tree = c.getTree(); + try { + repo.get(tree, submodule); + return true; + } catch (AssertionError e) { + return false; + } + } + +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java new file mode 100644 index 0000000..7823e7d --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -0,0 +1,90 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.git; + +import static com.google.gerrit.acceptance.GitUtil.getChangeId; + +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.extensions.api.changes.ReviewInput; +import com.google.gerrit.testutil.ConfigSuite; + +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.RefSpec; +import org.junit.Test; + +@NoHttpd +public class SubmoduleSubscriptionsWholeTopicMergeIT + extends AbstractSubmoduleSubscription { + + @ConfigSuite.Default + public static Config submitWholeTopicEnabled() { + return submitWholeTopicEnabledConfig(); + } + + @Test + public void testSubscriptionUpdateOfManyChanges() throws Exception { + TestRepository<?> superRepo = createProjectWithPush("super-project"); + TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project"); + createSubscription(superRepo, "master", "subscribed-to-project", "master"); + + ObjectId subHEAD = subRepo.branch("HEAD").commit().insertChangeId() + .message("some change") + .add("a.txt", "a contents ") + .create(); + subRepo.git().push().setRemote("origin").setRefSpecs( + new RefSpec("HEAD:refs/heads/master")).call(); + + RevCommit c = subRepo.getRevWalk().parseCommit(subHEAD); + + RevCommit c1 = subRepo.branch("HEAD").commit().insertChangeId() + .message("first change") + .add("asdf", "asdf\n") + .create(); + subRepo.git().push().setRemote("origin") + .setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo"))) + .call(); + + subRepo.reset(c.getId()); + RevCommit c2 = subRepo.branch("HEAD").commit().insertChangeId() + .message("qwerty") + .add("qwerty", "qwerty") + .create(); + + RevCommit c3 = subRepo.branch("HEAD").commit().insertChangeId() + .message("qwerty followup") + .add("qwerty", "qwerty\nqwerty\n") + .create(); + subRepo.git().push().setRemote("origin") + .setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo"))) + .call(); + + String id1 = getChangeId(subRepo, c1).get(); + String id2 = getChangeId(subRepo, c2).get(); + String id3 = getChangeId(subRepo, c3).get(); + gApi.changes().id(id1).current().review(ReviewInput.approve()); + gApi.changes().id(id2).current().review(ReviewInput.approve()); + gApi.changes().id(id3).current().review(ReviewInput.approve()); + + gApi.changes().id(id1).current().submit(); + ObjectId subRepoId = subRepo.git().fetch().setRemote("origin").call() + .getAdvertisedRef("refs/heads/master").getObjectId(); + + expectToHaveSubmoduleState(superRepo, "master", + "subscribed-to-project", subRepoId); + } +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/VisibleRefFilterIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/VisibleRefFilterIT.java index 4a66a4169..0b07b87 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/VisibleRefFilterIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/VisibleRefFilterIT.java
@@ -68,22 +68,35 @@ private AccountGroup.UUID admins; + private Change.Id c1; + private Change.Id c2; + private String r1; + private String r2; + @Before public void setUp() throws Exception { admins = groupCache.get(new AccountGroup.NameKey("Administrators")) .getGroupUUID(); - setUpChanges(); setUpPermissions(); + setUpChanges(); } private void setUpPermissions() throws Exception { + // Remove read permissions for all users besides admin. This method is + // idempotent, so is safe to call on every test setup. ProjectConfig pc = projectCache.checkedGet(allProjects).getConfig(); for (AccessSection sec : pc.getAccessSections()) { sec.removePermission(Permission.READ); } + Util.allow(pc, Permission.READ, admins, "refs/*"); saveProjectConfig(allProjects, pc); } + private static String changeRefPrefix(Change.Id id) { + String ps = new PatchSet.Id(id, 1).toRefName(); + return ps.substring(0, ps.length() - 1); + } + private void setUpChanges() throws Exception { gApi.projects() .name(project.get()) @@ -91,12 +104,16 @@ .create(new BranchInput()); allow(Permission.SUBMIT, admins, "refs/for/refs/heads/*"); - PushOneCommit.Result mr = pushFactory.create(db, admin.getIdent()) - .to(git, "refs/for/master%submit"); + PushOneCommit.Result mr = pushFactory.create(db, admin.getIdent(), testRepo) + .to("refs/for/master%submit"); mr.assertOkStatus(); - PushOneCommit.Result br = pushFactory.create(db, admin.getIdent()) - .to(git, "refs/for/branch%submit"); + c1 = mr.getChange().getId(); + r1 = changeRefPrefix(c1); + PushOneCommit.Result br = pushFactory.create(db, admin.getIdent(), testRepo) + .to("refs/for/branch%submit"); br.assertOkStatus(); + c2 = br.getChange().getId(); + r2 = changeRefPrefix(c2); Repository repo = repoManager.openRepository(project); try { @@ -126,10 +143,10 @@ assertRefs( "HEAD", - "refs/changes/01/1/1", - "refs/changes/01/1/meta", - "refs/changes/02/2/1", - "refs/changes/02/2/meta", + r1 + "1", + r1 + "meta", + r2 + "1", + r2 + "meta", "refs/heads/branch", "refs/heads/master", "refs/tags/branch-tag", @@ -143,10 +160,10 @@ assertRefs( "HEAD", - "refs/changes/01/1/1", - "refs/changes/01/1/meta", - "refs/changes/02/2/1", - "refs/changes/02/2/meta", + r1 + "1", + r1 + "meta", + r2 + "1", + r2 + "meta", "refs/heads/branch", "refs/heads/master", "refs/meta/config", @@ -161,8 +178,8 @@ assertRefs( "HEAD", - "refs/changes/01/1/1", - "refs/changes/01/1/meta", + r1 + "1", + r1 + "meta", "refs/heads/master", "refs/tags/master-tag"); } @@ -173,8 +190,8 @@ allow(Permission.READ, REGISTERED_USERS, "refs/heads/branch"); assertRefs( - "refs/changes/02/2/1", - "refs/changes/02/2/meta", + r2 + "1", + r2 + "meta", "refs/heads/branch", "refs/tags/branch-tag", // master branch is not visible but master-tag is reachable from branch @@ -187,53 +204,57 @@ allow(Permission.READ, REGISTERED_USERS, "refs/heads/master"); deny(Permission.READ, REGISTERED_USERS, "refs/heads/branch"); - Change c1 = db.changes().get(new Change.Id(1)); - PatchSet ps1 = db.patchSets().get(new PatchSet.Id(c1.getId(), 1)); + Change change1 = db.changes().get(c1); + PatchSet ps1 = db.patchSets().get(new PatchSet.Id(c1, 1)); // Admin's edit is not visible. setApiUser(admin); - editModifier.createEdit(c1, ps1); + editModifier.createEdit(change1, ps1); // User's edit is visible. setApiUser(user); - editModifier.createEdit(c1, ps1); + editModifier.createEdit(change1, ps1); assertRefs( "HEAD", - "refs/changes/01/1/1", - "refs/changes/01/1/meta", + r1 + "1", + r1 + "meta", "refs/heads/master", "refs/tags/master-tag", - "refs/users/01/1000001/edit-1/1"); + "refs/users/01/1000001/edit-" + c1.get() + "/1"); } @Test public void subsetOfRefsVisibleWithAccessDatabase() throws Exception { - deny(Permission.READ, REGISTERED_USERS, "refs/heads/master"); - allow(Permission.READ, REGISTERED_USERS, "refs/heads/branch"); - allowGlobalCapability(GlobalCapability.ACCESS_DATABASE, REGISTERED_USERS); + allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); + try { + deny(Permission.READ, REGISTERED_USERS, "refs/heads/master"); + allow(Permission.READ, REGISTERED_USERS, "refs/heads/branch"); - Change c1 = db.changes().get(new Change.Id(1)); - PatchSet ps1 = db.patchSets().get(new PatchSet.Id(c1.getId(), 1)); - setApiUser(admin); - editModifier.createEdit(c1, ps1); - setApiUser(user); - editModifier.createEdit(c1, ps1); + Change change1 = db.changes().get(c1); + PatchSet ps1 = db.patchSets().get(new PatchSet.Id(c1, 1)); + setApiUser(admin); + editModifier.createEdit(change1, ps1); + setApiUser(user); + editModifier.createEdit(change1, ps1); - assertRefs( - // Change 1 is visible due to accessDatabase capability, even though - // refs/heads/master is not. - "refs/changes/01/1/1", - "refs/changes/01/1/meta", - "refs/changes/02/2/1", - "refs/changes/02/2/meta", - "refs/heads/branch", - "refs/tags/branch-tag", - // See comment in subsetOfBranchesVisibleNotIncludingHead. - "refs/tags/master-tag", - // All edits are visible due to accessDatabase capability. - "refs/users/00/1000000/edit-1/1", - "refs/users/01/1000001/edit-1/1"); + assertRefs( + // Change 1 is visible due to accessDatabase capability, even though + // refs/heads/master is not. + r1 + "1", + r1 + "meta", + r2 + "1", + r2 + "meta", + "refs/heads/branch", + "refs/tags/branch-tag", + // See comment in subsetOfBranchesVisibleNotIncludingHead. + "refs/tags/master-tag", + // All edits are visible due to accessDatabase capability. + "refs/users/00/1000000/edit-" + c1.get() + "/1", + "refs/users/01/1000001/edit-" + c1.get() + "/1"); + } finally { + removeGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE); + } } /** @@ -259,7 +280,7 @@ } Splitter s = Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings(); - assertThat(filtered).containsSequence( - Ordering.natural().sortedCopy(s.split(out))); + assertThat(filtered).containsExactlyElementsIn( + Ordering.natural().sortedCopy(s.split(out))).inOrder(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/AccountAssert.java index c90924d..91ee332 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/AccountAssert.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -16,8 +16,13 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.common.base.Function; +import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.reviewdb.client.Account; + +import java.util.List; public class AccountAssert { @@ -26,4 +31,21 @@ assertThat(a.fullName).isEqualTo(ai.name); assertThat(a.email).isEqualTo(ai.email); } + + public static void assertAccountInfos(List<TestAccount> expected, + List<AccountInfo> actual) { + Iterable<Account.Id> expectedIds = TestAccount.ids(expected); + Iterable<Account.Id> actualIds = Iterables.transform( + actual, + new Function<AccountInfo, Account.Id>() { + @Override + public Account.Id apply(AccountInfo in) { + return new Account.Id(in._accountId); + } + }); + assertThat(actualIds).containsExactlyElementsIn(expectedIds).inOrder(); + for (int i = 0; i < expected.size(); i++) { + AccountAssert.assertAccountInfo(expected.get(i), actual.get(i)); + } + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java index 6ce96ee..3e7c2bf 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
@@ -24,51 +24,56 @@ import static com.google.gerrit.common.data.GlobalCapability.PRIORITY; import static com.google.gerrit.common.data.GlobalCapability.QUERY_LIMIT; import static com.google.gerrit.common.data.GlobalCapability.RUN_AS; +import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.data.Permission; -import com.google.gerrit.common.data.PermissionRange; -import com.google.gerrit.common.data.PermissionRule; -import com.google.gerrit.server.git.MetaDataUpdate; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.http.HttpStatus; -import org.eclipse.jgit.errors.ConfigInvalidException; import org.junit.Test; -import java.io.IOException; - public class CapabilitiesIT extends AbstractDaemonTest { @Test public void testCapabilitiesUser() throws Exception { - grantAllCapabilities(); - RestResponse r = - userSession.get("/accounts/self/capabilities"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - CapabilityInfo info = (new Gson()).fromJson(r.getReader(), - new TypeToken<CapabilityInfo>() {}.getType()); - for (String c : GlobalCapability.getAllNames()) { - if (ADMINISTRATE_SERVER.equals(c)) { - assertThat(info.administrateServer).isFalse(); - } else if (BATCH_CHANGES_LIMIT.equals(c)) { - assertThat(info.batchChangesLimit.min).isEqualTo((short) 0); - assertThat(info.batchChangesLimit.max).isEqualTo((short) DEFAULT_MAX_BATCH_CHANGES_LIMIT); - } else if (PRIORITY.equals(c)) { - assertThat(info.priority).isFalse(); - } else if (QUERY_LIMIT.equals(c)) { - assertThat(info.queryLimit.min).isEqualTo((short) 0); - assertThat(info.queryLimit.max).isEqualTo((short) DEFAULT_MAX_QUERY_LIMIT); - } else { - assert_().withFailureMessage(String.format("capability %s was not granted", c)) - .that((Boolean) CapabilityInfo.class.getField(c).get(info)).isTrue(); + Iterable<String> all = Iterables.filter(GlobalCapability.getAllNames(), + new Predicate<String>() { + @Override + public boolean apply(String in) { + return !ADMINISTRATE_SERVER.equals(in) && !PRIORITY.equals(in); + } + }); + + allowGlobalCapabilities(REGISTERED_USERS, all); + try { + RestResponse r = + userSession.get("/accounts/self/capabilities"); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + CapabilityInfo info = (new Gson()).fromJson(r.getReader(), + new TypeToken<CapabilityInfo>() {}.getType()); + for (String c : GlobalCapability.getAllNames()) { + if (ADMINISTRATE_SERVER.equals(c)) { + assertThat(info.administrateServer).isFalse(); + } else if (BATCH_CHANGES_LIMIT.equals(c)) { + assertThat(info.batchChangesLimit.min).isEqualTo((short) 0); + assertThat(info.batchChangesLimit.max).isEqualTo((short) DEFAULT_MAX_BATCH_CHANGES_LIMIT); + } else if (PRIORITY.equals(c)) { + assertThat(info.priority).isFalse(); + } else if (QUERY_LIMIT.equals(c)) { + assertThat(info.queryLimit.min).isEqualTo((short) 0); + assertThat(info.queryLimit.max).isEqualTo((short) DEFAULT_MAX_QUERY_LIMIT); + } else { + assert_().withFailureMessage(String.format("capability %s was not granted", c)) + .that((Boolean) CapabilityInfo.class.getField(c).get(info)).isTrue(); + } } + } finally { + removeGlobalCapabilities(REGISTERED_USERS, all); } } @@ -101,35 +106,4 @@ } } } - - /** - * Grant all global capabilities except ADMINISTRATE_SERVER and PRIORITY. - * Set the default ranges for range permissions. - */ - private void grantAllCapabilities() throws IOException, - ConfigInvalidException { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); - md.setMessage("Make super user"); - ProjectConfig config = ProjectConfig.read(md); - AccessSection s = config.getAccessSection( - AccessSection.GLOBAL_CAPABILITIES); - for (String c : GlobalCapability.getAllNames()) { - if (ADMINISTRATE_SERVER.equals(c) || PRIORITY.equals(c)) { - continue; - } - Permission p = s.getPermission(c, true); - PermissionRule rule = new PermissionRule( - config.resolve(SystemGroupBackend.getGroup( - SystemGroupBackend.REGISTERED_USERS))); - if (GlobalCapability.hasRange(c)) { - PermissionRange.WithDefaults range = GlobalCapability.getRange(c); - if (range != null) { - rule.setRange(range.getDefaultMin(), range.getDefaultMax()); - } - } - p.add(rule); - } - config.commit(md); - projectCache.evict(config.getProject()); - } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountIT.java index 63c6493..af90d8f 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -14,54 +14,51 @@ package com.google.gerrit.acceptance.rest.account; -import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfo; +import static org.junit.Assert.fail; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.TestAccount; -import com.google.gerrit.extensions.common.AccountInfo; -import com.google.gerrit.extensions.restapi.Url; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; - +@NoHttpd public class GetAccountIT extends AbstractDaemonTest { @Test public void getNonExistingAccount_NotFound() throws Exception { - assertThat(adminSession.get("/accounts/non-existing").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); + try { + gApi.accounts().id("non-existing").get(); + fail("Expected account to not exist"); + } catch (ResourceNotFoundException expected) { + // Expected. + } } @Test public void getAccount() throws Exception { // by formatted string - testGetAccount("/accounts/" - + Url.encode(admin.fullName + " <" + admin.email + ">"), admin); + testGetAccount(admin.fullName + " <" + admin.email + ">", admin); // by email - testGetAccount("/accounts/" + admin.email, admin); + testGetAccount(admin.email, admin); // by full name - testGetAccount("/accounts/" + admin.fullName, admin); + testGetAccount(admin.fullName, admin); // by account ID - testGetAccount("/accounts/" + admin.id.get(), admin); + testGetAccount(Integer.toString(admin.id.get()), admin); // by user name - testGetAccount("/accounts/" + admin.username, admin); + testGetAccount(admin.username, admin); // by 'self' - testGetAccount("/accounts/self", admin); + testGetAccount("self", admin); } - private void testGetAccount(String url, TestAccount expectedAccount) - throws IOException { - RestResponse r = adminSession.get(url); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertAccountInfo(expectedAccount, newGson() - .fromJson(r.getReader(), AccountInfo.class)); + private void testGetAccount(String id, TestAccount expectedAccount) + throws Exception { + assertAccountInfo(expectedAccount, gApi.accounts().id(id).get()); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java deleted file mode 100644 index face299a..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.account; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.PushOneCommit.Result; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.reviewdb.client.Change; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -import java.io.IOException; - -public class StarredChangesIT extends AbstractDaemonTest { - - @Test - public void starredChangeState() throws Exception { - Result c1 = createChange(); - Result c2 = createChange(); - assertThat(getChange(c1.getChangeId()).starred).isNull(); - assertThat(getChange(c2.getChangeId()).starred).isNull(); - starChange(true, c1.getPatchSetId().getParentKey()); - starChange(true, c2.getPatchSetId().getParentKey()); - assertThat(getChange(c1.getChangeId()).starred).isTrue(); - assertThat(getChange(c2.getChangeId()).starred).isTrue(); - starChange(false, c1.getPatchSetId().getParentKey()); - starChange(false, c2.getPatchSetId().getParentKey()); - assertThat(getChange(c1.getChangeId()).starred).isNull(); - assertThat(getChange(c2.getChangeId()).starred).isNull(); - } - - private void starChange(boolean on, Change.Id id) throws IOException { - String url = "/accounts/self/starred.changes/" + id.get(); - if (on) { - RestResponse r = adminSession.put(url); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - } else { - RestResponse r = adminSession.delete(url); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - } - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java index dec3e65..a07bde7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -18,23 +18,24 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assert_; import static com.google.common.truth.TruthJUnit.assume; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION; import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.SshSession; +import com.google.gerrit.acceptance.TestProjectInput; import com.google.gerrit.common.EventListener; import com.google.gerrit.common.EventSource; -import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.api.changes.SubmitInput; +import com.google.gerrit.extensions.api.projects.BranchInfo; +import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.client.InheritableBoolean; +import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.LabelInfo; @@ -50,19 +51,14 @@ import com.google.gerrit.server.events.ChangeMergedEvent; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.notedb.ChangeNotes; -import com.google.gerrit.server.project.ListBranches.BranchInfo; -import com.google.gerrit.server.project.PutConfig; import com.google.gerrit.testutil.ConfigSuite; import com.google.gson.reflect.TypeToken; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; -import com.jcraft.jsch.JSchException; - import org.apache.http.HttpStatus; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -116,7 +112,6 @@ } }, listenerUser); - project = new Project.NameKey("p2"); } @After @@ -127,10 +122,9 @@ protected abstract SubmitType getSubmitType(); @Test - public void submitToEmptyRepo() throws JSchException, IOException, - GitAPIException { - Git git = createProject(false); - PushOneCommit.Result change = createChange(git); + @TestProjectInput(createEmptyCommit = false) + public void submitToEmptyRepo() throws Exception { + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); assertThat(getRemoteHead().getId()).isEqualTo(change.getCommitId()); } @@ -138,85 +132,69 @@ @Test public void submitWholeTopic() throws Exception { assume().that(isSubmitWholeTopicEnabled()).isTrue(); - Git git = createProject(); PushOneCommit.Result change1 = - createChange(git, "Change 1", "a.txt", "content", "test-topic"); + createChange("Change 1", "a.txt", "content", "test-topic"); PushOneCommit.Result change2 = - createChange(git, "Change 2", "b.txt", "content", "test-topic"); + createChange("Change 2", "b.txt", "content", "test-topic"); + PushOneCommit.Result change3 = + createChange("Change 3", "c.txt", "content", "test-topic"); approve(change1.getChangeId()); approve(change2.getChangeId()); - submit(change2.getChangeId()); + approve(change3.getChangeId()); + submit(change3.getChangeId()); change1.assertChange(Change.Status.MERGED, "test-topic", admin); change2.assertChange(Change.Status.MERGED, "test-topic", admin); + change3.assertChange(Change.Status.MERGED, "test-topic", admin); + assertSubmitter(change1); + assertSubmitter(change2); + assertSubmitter(change3); } - protected Git createProject() throws JSchException, IOException, - GitAPIException { - return createProject(true); - } - - private Git createProject(boolean emptyCommit) - throws JSchException, IOException, GitAPIException { - SshSession sshSession = new SshSession(server, admin); - try { - GitUtil.createProject(sshSession, project.get(), null, emptyCommit); - setSubmitType(getSubmitType()); - return cloneProject(sshSession.getUrl() + "/" + project.get()); - } finally { - sshSession.close(); + private void assertSubmitter(PushOneCommit.Result change) throws Exception { + ChangeInfo info = get(change.getChangeId(), ListChangesOption.MESSAGES); + assertThat(info.messages).isNotNull(); + assertThat(info.messages).hasSize(3); + if (getSubmitType() == SubmitType.CHERRY_PICK) { + assertThat(Iterables.getLast(info.messages).message).startsWith( + "Change has been successfully cherry-picked as "); + } else { + assertThat(Iterables.getLast(info.messages).message).isEqualTo( + "Change has been successfully merged into the git repository by Administrator"); } } - private void setSubmitType(SubmitType submitType) throws IOException { - PutConfig.Input in = new PutConfig.Input(); - in.submitType = submitType; - in.useContentMerge = InheritableBoolean.FALSE; - RestResponse r = - adminSession.put("/projects/" + project.get() + "/config", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); + @Override + protected void updateProjectInput(ProjectInput in) { + in.submitType = getSubmitType(); + if (in.useContentMerge == InheritableBoolean.INHERIT) { + in.useContentMerge = InheritableBoolean.FALSE; + } } - protected void setUseContentMerge() throws IOException { - PutConfig.Input in = new PutConfig.Input(); - in.useContentMerge = InheritableBoolean.TRUE; - RestResponse r = - adminSession.put("/projects/" + project.get() + "/config", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); - } - - protected PushOneCommit.Result createChange(Git git) throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - return push.to(git, "refs/for/master"); - } - - protected PushOneCommit.Result createChange(Git git, String subject, - String fileName, String content) throws GitAPIException, IOException { + protected PushOneCommit.Result createChange(String subject, + String fileName, String content) throws Exception { PushOneCommit push = - pushFactory.create(db, admin.getIdent(), subject, fileName, content); - return push.to(git, "refs/for/master"); + pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content); + return push.to("refs/for/master"); } - protected PushOneCommit.Result createChange(Git git, String subject, + protected PushOneCommit.Result createChange(String subject, String fileName, String content, String topic) - throws GitAPIException, IOException { + throws Exception { PushOneCommit push = - pushFactory.create(db, admin.getIdent(), subject, fileName, content); - return push.to(git, "refs/for/master/" + topic); + pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content); + return push.to("refs/for/master/" + topic); } - protected void submit(String changeId) throws IOException { + protected void submit(String changeId) throws Exception { submit(changeId, HttpStatus.SC_OK); } - protected void submitWithConflict(String changeId) throws IOException { + protected void submitWithConflict(String changeId) throws Exception { submit(changeId, HttpStatus.SC_CONFLICT); } - protected void submitStatusOnly(String changeId) - throws IOException, OrmException { + protected void submitStatusOnly(String changeId) throws Exception { approve(changeId); Change c = queryProvider.get().byKeyPrefix(changeId).get(0).change(); c.setStatus(Change.Status.SUBMITTED); @@ -232,7 +210,7 @@ indexer.index(db, c); } - private void submit(String changeId, int expectedStatus) throws IOException { + private void submit(String changeId, int expectedStatus) throws Exception { approve(changeId); SubmitInput subm = new SubmitInput(); subm.waitForMerge = true; @@ -266,17 +244,9 @@ b.consume(); } - private void approve(String changeId) throws IOException { - RestResponse r = adminSession.post( - "/changes/" + changeId + "/revisions/current/review", - new ReviewInput().label("Code-Review", 2)); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); - } - protected void assertCurrentRevision(String changeId, int expectedNum, - ObjectId expectedId) throws IOException { - ChangeInfo c = getChange(changeId, CURRENT_REVISION); + ObjectId expectedId) throws Exception { + ChangeInfo c = get(changeId, CURRENT_REVISION); assertThat(c.currentRevision).isEqualTo(expectedId.name()); assertThat(c.revisions.get(expectedId.name())._number).isEqualTo(expectedNum); Repository repo = @@ -291,11 +261,11 @@ } } - protected void assertApproved(String changeId) throws IOException { - ChangeInfo c = getChange(changeId, DETAILED_LABELS); + protected void assertApproved(String changeId) throws Exception { + ChangeInfo c = get(changeId, DETAILED_LABELS); LabelInfo cr = c.labels.get("Code-Review"); assertThat(cr.all).hasSize(1); - assertThat(cr.all.get(0).value.intValue()).isEqualTo(2); + assertThat(cr.all.get(0).value).isEqualTo(2); assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(admin.getId()); } @@ -309,17 +279,17 @@ assertThat(submitter.getAccountId()).isEqualTo(admin.getId()); } - protected void assertCherryPick(Git localGit, boolean contentMerge) - throws IOException { - assertRebase(localGit, contentMerge); + protected void assertCherryPick(TestRepository<?> testRepo, + boolean contentMerge) throws IOException { + assertRebase(testRepo, contentMerge); RevCommit remoteHead = getRemoteHead(); assertThat(remoteHead.getFooterLines("Reviewed-On")).isNotEmpty(); assertThat(remoteHead.getFooterLines("Reviewed-By")).isNotEmpty(); } - protected void assertRebase(Git localGit, boolean contentMerge) + protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws IOException { - Repository repo = localGit.getRepository(); + Repository repo = testRepo.getRepository(); RevCommit localHead = getHead(repo); RevCommit remoteHead = getRemoteHead(); assert_().withFailureMessage(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java index 1d60607..98aae37 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -15,34 +15,27 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.extensions.client.InheritableBoolean; -import com.jcraft.jsch.JSchException; - -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; -import java.io.IOException; - public abstract class AbstractSubmitByMerge extends AbstractSubmit { @Test - public void submitWithMerge() throws JSchException, IOException, - GitAPIException { - Git git = createProject(); + public void submitWithMerge() throws Exception { RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "b.txt", "other content"); + createChange("Change 2", "b.txt", "other content"); submit(change2.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getParentCount()).isEqualTo(2); @@ -51,21 +44,19 @@ } @Test - public void submitWithContentMerge() throws JSchException, IOException, - GitAPIException { - Git git = createProject(); - setUseContentMerge(); + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) + public void submitWithContentMerge() throws Exception { PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n"); + createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n"); submit(change.getChangeId()); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); + createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); submit(change2.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, change.getCommitId().getName()); + testRepo.reset(change.getCommitId()); PushOneCommit.Result change3 = - createChange(git, "Change 3", "a.txt", "bbb\nccc\n"); + createChange("Change 3", "a.txt", "bbb\nccc\n"); submit(change3.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getParentCount()).isEqualTo(2); @@ -74,19 +65,17 @@ } @Test - public void submitWithContentMerge_Conflict() throws JSchException, - IOException, GitAPIException { - Git git = createProject(); - setUseContentMerge(); + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) + public void submitWithContentMerge_Conflict() throws Exception { RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "other content"); + createChange("Change 2", "a.txt", "other content"); submitWithConflict(change2.getChangeId()); assertThat(getRemoteHead()).isEqualTo(oldHead); }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java index afcd5d0..66c99f1 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -18,18 +18,15 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.testutil.ConfigSuite; -import com.google.gson.reflect.TypeToken; -import org.apache.http.HttpStatus; -import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; import org.junit.Test; -import java.io.IOException; import java.util.Map; public class ActionsIT extends AbstractDaemonTest { @@ -40,7 +37,7 @@ @Test public void revisionActionsOneChangePerTopicUnapproved() throws Exception { - String changeId = createChangeWithTopic("foo1").getChangeId(); + String changeId = createChangeWithTopic().getChangeId(); Map<String, ActionInfo> actions = getActions(changeId); assertThat(actions).containsKey("cherrypick"); assertThat(actions).containsKey("rebase"); @@ -49,27 +46,21 @@ @Test public void revisionActionsOneChangePerTopic() throws Exception { - String changeId = createChangeWithTopic("foo1").getChangeId(); + String changeId = createChangeWithTopic().getChangeId(); approve(changeId); Map<String, ActionInfo> actions = getActions(changeId); commonActionsAssertions(actions); - if (isSubmitWholeTopicEnabled()) { - ActionInfo info = actions.get("submit"); - assertThat(info.enabled).isTrue(); - assertThat(info.label).isEqualTo("Submit whole topic"); - assertThat(info.method).isEqualTo("POST"); - assertThat(info.title).isEqualTo("Submit all 1 changes of the same topic"); - } else { - noSubmitWholeTopicAssertions(actions); - } + // We want to treat a single change in a topic not as a whole topic, + // so regardless of how submitWholeTopic is configured: + noSubmitWholeTopicAssertions(actions); } @Test public void revisionActionsTwoChangeChangesInTopic() throws Exception { - String changeId = createChangeWithTopic("foo2").getChangeId(); + String changeId = createChangeWithTopic().getChangeId(); approve(changeId); // create another change with the same topic - createChangeWithTopic("foo2").getChangeId(); + createChangeWithTopic().getChangeId(); Map<String, ActionInfo> actions = getActions(changeId); commonActionsAssertions(actions); if (isSubmitWholeTopicEnabled()) { @@ -84,11 +75,42 @@ } @Test + public void revisionActionsTwoChangeChangesInTopic_conflicting() throws Exception { + String changeId = createChangeWithTopic().getChangeId(); + approve(changeId); + + // create another change with the same topic + String changeId2 = createChangeWithTopic(testRepo, "foo2", "touching b", + "b.txt", "real content").getChangeId(); + approve(changeId2); + + // collide with the other change in the same topic + testRepo.reset("HEAD~2"); + String collidingChange = createChangeWithTopic(testRepo, "off_topic", + "rewriting file b", "b.txt", "garbage\ngarbage\ngarbage").getChangeId(); + gApi.changes().id(collidingChange).current().review(ReviewInput.approve()); + gApi.changes().id(collidingChange).current().submit(); + + Map<String, ActionInfo> actions = getActions(changeId); + commonActionsAssertions(actions); + if (isSubmitWholeTopicEnabled()) { + ActionInfo info = actions.get("submit"); + assertThat(info.enabled).isNull(); + assertThat(info.label).isEqualTo("Submit whole topic"); + assertThat(info.method).isEqualTo("POST"); + assertThat(info.title).isEqualTo( + "Clicking the button would fail for other changes in the topic"); + } else { + noSubmitWholeTopicAssertions(actions); + } + } + + @Test public void revisionActionsTwoChangeChangesInTopicReady() throws Exception { - String changeId = createChangeWithTopic("foo2").getChangeId(); + String changeId = createChangeWithTopic().getChangeId(); approve(changeId); // create another change with the same topic - String changeId2 = createChangeWithTopic("foo2").getChangeId(); + String changeId2 = createChangeWithTopic().getChangeId(); approve(changeId2); Map<String, ActionInfo> actions = getActions(changeId); commonActionsAssertions(actions); @@ -103,15 +125,6 @@ } } - private Map<String, ActionInfo> getActions(String changeId) - throws IOException { - return newGson().fromJson( - adminSession.get("/changes/" - + changeId - + "/revisions/1/actions").getReader(), - new TypeToken<Map<String, ActionInfo>>() {}.getType()); - } - private void noSubmitWholeTopicAssertions(Map<String, ActionInfo> actions) { ActionInfo info = actions.get("submit"); assertThat(info.enabled).isTrue(); @@ -127,18 +140,18 @@ assertThat(actions).containsKey("rebase"); } - private PushOneCommit.Result createChangeWithTopic(String topic) throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); + private PushOneCommit.Result createChangeWithTopic( + TestRepository<InMemoryRepository> repo, String topic, + String commitMsg, String fileName, String content) throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), + repo, commitMsg, fileName, content); assertThat(topic).isNotEmpty(); - return push.to(git, "refs/for/master/" + topic); + return push.to("refs/for/master/" + name(topic)); } - private void approve(String changeId) throws IOException { - RestResponse r = adminSession.post( - "/changes/" + changeId + "/revisions/current/review", - new ReviewInput().label("Code-Review", 2)); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); + private PushOneCommit.Result createChangeWithTopic() + throws Exception { + return createChangeWithTopic(testRepo, "foo2", + "a message", "a.txt", "content\n"); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java index 3c76355..a56a7f2 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -73,15 +73,15 @@ String changeId = createChange().getChangeId(); postMessage(changeId, "Some nits need to be fixed."); ChangeInfo c = info(changeId); - assertThat((Iterable<?>)c.messages).isNull(); + assertThat(c.messages).isNull(); } @Test public void defaultMessage() throws Exception { String changeId = createChange().getChangeId(); ChangeInfo c = get(changeId); - assertThat((Iterable<?>)c.messages).isNotNull(); - assertThat((Iterable<?>)c.messages).hasSize(1); + assertThat(c.messages).isNotNull(); + assertThat(c.messages).hasSize(1); assertThat(c.messages.iterator().next().message) .isEqualTo("Uploaded patch set 1."); } @@ -94,8 +94,8 @@ String secondMessage = "I like this feature."; postMessage(changeId, secondMessage); ChangeInfo c = get(changeId); - assertThat((Iterable<?>)c.messages).isNotNull(); - assertThat((Iterable<?>)c.messages).hasSize(3); + assertThat(c.messages).isNotNull(); + assertThat(c.messages).hasSize(3); Iterator<ChangeMessageInfo> it = c.messages.iterator(); assertThat(it.next().message).isEqualTo("Uploaded patch set 1."); assertMessage(firstMessage, it.next().message);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java index 8e7daec..2229577 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
@@ -14,27 +14,22 @@ package com.google.gerrit.acceptance.rest.change; -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; -import static com.google.gerrit.acceptance.GitUtil.initSsh; import static com.google.gerrit.common.data.Permission.LABEL; import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context; import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.RestSession; -import com.google.gerrit.acceptance.SshSession; import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.acceptance.TestProjectInput; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.extensions.api.changes.ReviewInput; +import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.group.SystemGroupBackend; -import org.apache.http.HttpStatus; -import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.ConfigInvalidException; import org.junit.Before; import org.junit.Test; @@ -45,45 +40,47 @@ private TestAccount user2; - private RestSession sessionOwner; - private RestSession sessionDev; - @Before public void setUp() throws Exception { - sessionOwner = new RestSession(server, user); - SshSession sshSession = new SshSession(server, user); - initSsh(user); - sshSession.open(); - git = cloneProject(sshSession.getUrl() + "/" + project.get()); - sshSession.close(); + setApiUser(user); user2 = accounts.user2(); - sessionDev = new RestSession(server, user2); } @Test + @TestProjectInput(cloneAs = "user") public void testChangeOwner_OwnerACLNotGranted() throws Exception { - approve(sessionOwner, createMyChange(), HttpStatus.SC_FORBIDDEN); + assertApproveFails(user, createMyChange()); } @Test + @TestProjectInput(cloneAs = "user") public void testChangeOwner_OwnerACLGranted() throws Exception { grantApproveToChangeOwner(); - approve(sessionOwner, createMyChange(), HttpStatus.SC_OK); + approve(user, createMyChange()); } @Test + @TestProjectInput(cloneAs = "user") public void testChangeOwner_NotOwnerACLGranted() throws Exception { grantApproveToChangeOwner(); - approve(sessionDev, createMyChange(), HttpStatus.SC_FORBIDDEN); + assertApproveFails(user2, createMyChange()); } - private void approve(RestSession s, String changeId, int expected) - throws IOException { - RestResponse r = - s.post("/changes/" + changeId + "/revisions/current/review", - new ReviewInput().label("Code-Review", 2)); - assertThat(r.getStatusCode()).isEqualTo(expected); - r.consume(); + private void approve(TestAccount a, String changeId) throws Exception { + Context old = setApiUser(a); + try { + gApi.changes().id(changeId).current().review(ReviewInput.approve()); + } finally { + atrScope.set(old); + } + } + + private void assertApproveFails(TestAccount a, String changeId) throws Exception { + try { + approve(a, changeId); + } catch (AuthException expected) { + // Expected. + } } private void grantApproveToChangeOwner() throws IOException, @@ -102,9 +99,8 @@ projectCache.evict(config.getProject()); } - private String createMyChange() throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, user.getIdent()); - return push.to(git, "refs/for/master").getChangeId(); + private String createMyChange() throws Exception { + PushOneCommit push = pushFactory.create(db, user.getIdent(), testRepo); + return push.to("refs/for/master").getChangeId(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java deleted file mode 100644 index 6a1c0a6..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ConflictsOperatorIT.java +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.change; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; - -import com.google.common.base.Function; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.extensions.common.ChangeInfo; -import com.google.gson.reflect.TypeToken; - -import org.apache.http.HttpStatus; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.junit.Test; - -import java.io.IOException; -import java.util.Set; - -public class ConflictsOperatorIT extends AbstractDaemonTest { - - private int count; - - @Test - public void noConflictingChanges() throws Exception { - PushOneCommit.Result change = createChange(git, true); - createChange(git, false); - - Set<String> changes = queryConflictingChanges(change); - assertThat((Iterable<?>)changes).isEmpty(); - } - - @Test - public void conflictingChanges() throws Exception { - PushOneCommit.Result change = createChange(git, true); - PushOneCommit.Result conflictingChange1 = createChange(git, true); - PushOneCommit.Result conflictingChange2 = createChange(git, true); - createChange(git, false); - - Set<String> changes = queryConflictingChanges(change); - assertChanges(changes, conflictingChange1, conflictingChange2); - } - - private PushOneCommit.Result createChange(Git git, boolean conflicting) - throws GitAPIException, IOException { - checkout(git, "origin/master"); - String file = conflicting ? "test.txt" : "test-" + count + ".txt"; - PushOneCommit push = - pushFactory.create(db, admin.getIdent(), "Change " + count, file, - "content " + count); - count++; - return push.to(git, "refs/for/master"); - } - - private Set<String> queryConflictingChanges(PushOneCommit.Result change) - throws IOException { - RestResponse r = - adminSession.get("/changes/?q=conflicts:" + change.getChangeId()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Set<ChangeInfo> changes = - newGson().fromJson(r.getReader(), - new TypeToken<Set<ChangeInfo>>() {}.getType()); - r.consume(); - return ImmutableSet.copyOf(Iterables.transform(changes, - new Function<ChangeInfo, String>() { - @Override - public String apply(ChangeInfo input) { - return input.id; - } - })); - } - - private void assertChanges(Set<String> actualChanges, - PushOneCommit.Result... expectedChanges) { - assertThat((Iterable<?>)actualChanges).hasSize(expectedChanges.length); - for (PushOneCommit.Result c : expectedChanges) { - assertThat(actualChanges.contains(id(c))).isTrue(); - } - } - - private String id(PushOneCommit.Result change) { - return project.get() + "~master~" + change.getChangeId(); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java index 311161a..d92270c 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -16,18 +16,22 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.fail; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; +import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.testutil.ConfigSuite; -import org.apache.http.HttpStatus; import org.eclipse.jgit.lib.Config; import org.junit.Test; +@NoHttpd public class CreateChangeIT extends AbstractDaemonTest { @ConfigSuite.Config public static Config allowDraftsDisabled() { @@ -38,9 +42,8 @@ public void createEmptyChange_MissingBranch() throws Exception { ChangeInfo ci = new ChangeInfo(); ci.project = project.get(); - RestResponse r = adminSession.post("/changes/", ci); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); - assertThat(r.getEntityContent()).contains("branch must be non-empty"); + assertCreateFails(ci, BadRequestException.class, + "branch must be non-empty"); } @Test @@ -48,37 +51,34 @@ ChangeInfo ci = new ChangeInfo(); ci.project = project.get(); ci.branch = "master"; - RestResponse r = adminSession.post("/changes/", ci); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); - assertThat(r.getEntityContent()).contains("commit message must be non-empty"); + assertCreateFails(ci, BadRequestException.class, + "commit message must be non-empty"); } @Test public void createEmptyChange_InvalidStatus() throws Exception { ChangeInfo ci = newChangeInfo(ChangeStatus.SUBMITTED); - RestResponse r = adminSession.post("/changes/", ci); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); - assertThat(r.getEntityContent()).contains("unsupported change status"); + assertCreateFails(ci, BadRequestException.class, + "unsupported change status"); } @Test public void createNewChange() throws Exception { - assertChange(newChangeInfo(ChangeStatus.NEW)); + assertCreateSucceeds(newChangeInfo(ChangeStatus.NEW)); } @Test public void createDraftChange() throws Exception { assume().that(isAllowDrafts()).isTrue(); - assertChange(newChangeInfo(ChangeStatus.DRAFT)); + assertCreateSucceeds(newChangeInfo(ChangeStatus.DRAFT)); } @Test public void createDraftChangeNotAllowed() throws Exception { assume().that(isAllowDrafts()).isFalse(); ChangeInfo ci = newChangeInfo(ChangeStatus.DRAFT); - RestResponse r = adminSession.post("/changes/", ci); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_METHOD_NOT_ALLOWED); - assertThat(r.getEntityContent()).contains("draft workflow is disabled"); + assertCreateFails(ci, MethodNotAllowedException.class, + "draft workflow is disabled"); } private ChangeInfo newChangeInfo(ChangeStatus status) { @@ -91,13 +91,8 @@ return in; } - private void assertChange(ChangeInfo in) throws Exception { - RestResponse r = adminSession.post("/changes/", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - - ChangeInfo info = newGson().fromJson(r.getReader(), ChangeInfo.class); - ChangeInfo out = get(info.changeId); - + private void assertCreateSucceeds(ChangeInfo in) throws Exception { + ChangeInfo out = gApi.changes().create(in).get(); assertThat(out.branch).isEqualTo(in.branch); assertThat(out.subject).isEqualTo(in.subject); assertThat(out.topic).isEqualTo(in.topic); @@ -107,6 +102,18 @@ assertThat(booleanToDraftStatus(draft)).isEqualTo(in.status); } + private void assertCreateFails(ChangeInfo in, + Class<? extends RestApiException> errType, String errSubstring) + throws Exception { + try { + gApi.changes().create(in); + fail("Expected " + errType.getSimpleName()); + } catch (RestApiException expected) { + assertThat(expected).isInstanceOf(errType); + assertThat(expected.getMessage()).contains(errSubstring); + } + } + private ChangeStatus booleanToDraftStatus(Boolean draft) { if (draft == null) { return ChangeStatus.NEW;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java index 871b1cc..47d071f 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
@@ -29,7 +29,6 @@ import com.google.gwtorm.server.OrmException; import org.apache.http.HttpStatus; -import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.Test; import java.io.IOException; @@ -40,7 +39,7 @@ public void deletePatchSet() throws Exception { String changeId = createChange().getChangeId(); PatchSet ps = getCurrentPatchSet(changeId); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.NEW); @@ -53,7 +52,7 @@ public void deleteDraftPatchSetNoACL() throws Exception { String changeId = createDraftChangeWith2PS(); PatchSet ps = getCurrentPatchSet(changeId); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.DRAFT); @@ -66,26 +65,25 @@ public void deleteDraftPatchSetAndChange() throws Exception { String changeId = createDraftChangeWith2PS(); PatchSet ps = getCurrentPatchSet(changeId); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.DRAFT); RestResponse r = deletePatchSet(changeId, ps, adminSession); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - assertThat(getChange(changeId).patches().size()).isEqualTo(1); + assertThat(getChange(changeId).patchSets()).hasSize(1); ps = getCurrentPatchSet(changeId); r = deletePatchSet(changeId, ps, adminSession); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); assertThat(queryProvider.get().byKeyPrefix(changeId)).isEmpty(); } - private String createDraftChangeWith2PS() throws GitAPIException, - IOException { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - Result result = push.to(git, "refs/drafts/master"); - push = pushFactory.create(db, admin.getIdent(), PushOneCommit.SUBJECT, + private String createDraftChangeWith2PS() throws Exception { + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + Result result = push.to("refs/drafts/master"); + push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", result.getChangeId()); - return push.to(git, "refs/drafts/master").getChangeId(); + return push.to("refs/drafts/master").getChangeId(); } private PatchSet getCurrentPatchSet(String changeId) throws OrmException {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java index a3809a9..834b873 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
@@ -45,7 +45,7 @@ PushOneCommit.Result result = createChange(); result.assertOkStatus(); String changeId = result.getChangeId(); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.NEW); @@ -60,7 +60,7 @@ PushOneCommit.Result result = createDraftChange(); result.assertOkStatus(); String changeId = result.getChangeId(); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.DRAFT); @@ -74,7 +74,7 @@ PushOneCommit.Result result = createDraftChange(); result.assertOkStatus(); String changeId = result.getChangeId(); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.DRAFT); @@ -90,7 +90,7 @@ PushOneCommit.Result result = createDraftChange(); result.assertOkStatus(); String changeId = result.getChangeId(); - String triplet = "p~master~" + changeId; + String triplet = project.get() + "~master~" + changeId; ChangeInfo c = get(triplet); assertThat(c.id).isEqualTo(triplet); assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java index 26d6a1e..6ef53ff 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -16,218 +16,206 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.common.base.CharMatcher; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.truth.IterableSubject; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.extensions.api.changes.HashtagsInput; import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.testutil.ConfigSuite; -import com.google.gson.reflect.TypeToken; -import org.apache.http.HttpStatus; import org.eclipse.jgit.lib.Config; import org.junit.Test; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - +@NoHttpd public class HashtagsIT extends AbstractDaemonTest { @ConfigSuite.Default public static Config defaultConfig() { return NotesMigration.allEnabledConfig(); } - private void assertResult(RestResponse r, List<String> expected) - throws IOException { - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - List<String> result = toHashtagList(r); - assertThat(result).containsExactlyElementsIn(expected); - } - @Test public void testGetNoHashtags() throws Exception { - // GET hashtags on a change with no hashtags returns an empty list - String changeId = createChange().getChangeId(); - assertResult(GET(changeId), ImmutableList.<String>of()); + // Get on a change with no hashtags returns an empty list. + PushOneCommit.Result r = createChange(); + assertThatGet(r).isEmpty(); } @Test public void testAddSingleHashtag() throws Exception { - String changeId = createChange().getChangeId(); + PushOneCommit.Result r = createChange(); - // POST adding a single hashtag returns a single hashtag - List<String> expected = Arrays.asList("tag2"); - assertResult(POST(changeId, "tag2", null), expected); - assertResult(GET(changeId), expected); + // Adding a single hashtag returns a single hashtag. + addHashtags(r, "tag2"); + assertThatGet(r).containsExactly("tag2"); - // POST adding another single hashtag to change that already has one - // hashtag returns a sorted list of hashtags with existing and new - expected = Arrays.asList("tag1", "tag2"); - assertResult(POST(changeId, "tag1", null), expected); - assertResult(GET(changeId), expected); + // Adding another single hashtag to change that already has one hashtag + // returns a sorted list of hashtags with existing and new. + addHashtags(r, "tag1"); + assertThatGet(r).containsExactly("tag1", "tag2").inOrder(); } @Test public void testAddMultipleHashtags() throws Exception { - String changeId = createChange().getChangeId(); + PushOneCommit.Result r = createChange(); - // POST adding multiple hashtags returns a sorted list of hashtags - List<String> expected = Arrays.asList("tag1", "tag3"); - assertResult(POST(changeId, "tag3, tag1", null), expected); - assertResult(GET(changeId), expected); + // Adding multiple hashtags returns a sorted list of hashtags. + addHashtags(r, "tag3", "tag1"); + assertThatGet(r).containsExactly("tag1", "tag3").inOrder(); - // POST adding multiple hashtags to change that already has hashtags - // returns a sorted list of hashtags with existing and new - expected = Arrays.asList("tag1", "tag2", "tag3", "tag4"); - assertResult(POST(changeId, "tag2, tag4", null), expected); - assertResult(GET(changeId), expected); + // Adding multiple hashtags to change that already has hashtags returns a + // sorted list of hashtags with existing and new. + addHashtags(r, "tag2", "tag4"); + assertThatGet(r).containsExactly("tag1", "tag2", "tag3", "tag4").inOrder(); } @Test public void testAddAlreadyExistingHashtag() throws Exception { - // POST adding a hashtag that already exists on the change returns a - // sorted list of hashtags without duplicates - String changeId = createChange().getChangeId(); - List<String> expected = Arrays.asList("tag2"); - assertResult(POST(changeId, "tag2", null), expected); - assertResult(GET(changeId), expected); - assertResult(POST(changeId, "tag2", null), expected); - assertResult(GET(changeId), expected); - expected = Arrays.asList("tag1", "tag2"); - assertResult(POST(changeId, "tag2, tag1", null), expected); - assertResult(GET(changeId), expected); + // Adding a hashtag that already exists on the change returns a sorted list + // of hashtags without duplicates. + PushOneCommit.Result r = createChange(); + addHashtags(r, "tag2"); + assertThatGet(r).containsExactly("tag2"); + addHashtags(r, "tag2"); + assertThatGet(r).containsExactly("tag2"); + addHashtags(r, "tag1", "tag2"); + assertThatGet(r).containsExactly("tag1", "tag2").inOrder(); } @Test public void testHashtagsWithPrefix() throws Exception { - String changeId = createChange().getChangeId(); + PushOneCommit.Result r = createChange(); - // Leading # is stripped from added tag - List<String> expected = Arrays.asList("tag1"); - assertResult(POST(changeId, "#tag1", null), expected); - assertResult(GET(changeId), expected); + // Leading # is stripped from added tag. + addHashtags(r, "#tag1"); + assertThatGet(r).containsExactly("tag1"); - // Leading # is stripped from multiple added tags - expected = Arrays.asList("tag1", "tag2", "tag3"); - assertResult(POST(changeId, "#tag2, #tag3", null), expected); - assertResult(GET(changeId), expected); + // Leading # is stripped from multiple added tags. + addHashtags(r, "#tag2", "#tag3"); + assertThatGet(r).containsExactly("tag1", "tag2", "tag3").inOrder(); - // Leading # is stripped from removed tag - expected = Arrays.asList("tag1", "tag3"); - assertResult(POST(changeId, null, "#tag2"), expected); - assertResult(GET(changeId), expected); + // Leading # is stripped from removed tag. + removeHashtags(r, "#tag2"); + assertThatGet(r).containsExactly("tag1", "tag3").inOrder(); - // Leading # is stripped from multiple removed tags - expected = Collections.emptyList(); - assertResult(POST(changeId, null, "#tag1, #tag3"), expected); - assertResult(GET(changeId), expected); + // Leading # is stripped from multiple removed tags. + removeHashtags(r, "#tag1", "#tag3"); + assertThatGet(r).isEmpty(); - // Leading # and space are stripped from added tag - expected = Arrays.asList("tag1"); - assertResult(POST(changeId, "# tag1", null), expected); - assertResult(GET(changeId), expected); + // Leading # and space are stripped from added tag. + addHashtags(r, "# tag1"); + assertThatGet(r).containsExactly("tag1"); - // Multiple leading # are stripped from added tag - expected = Arrays.asList("tag1", "tag2"); - assertResult(POST(changeId, "##tag2", null), expected); - assertResult(GET(changeId), expected); + // Multiple leading # are stripped from added tag. + addHashtags(r, "##tag2"); + assertThatGet(r).containsExactly("tag1", "tag2").inOrder(); - // Multiple leading spaces and # are stripped from added tag - expected = Arrays.asList("tag1", "tag2", "tag3"); - assertResult(POST(changeId, " # # tag3", null), expected); - assertResult(GET(changeId), expected); + // Multiple leading spaces and # are stripped from added tag. + addHashtags(r, "# # tag3"); + assertThatGet(r).containsExactly("tag1", "tag2", "tag3").inOrder(); } @Test public void testRemoveSingleHashtag() throws Exception { - // POST removing a single tag from a change that only has that tag - // returns an empty list - String changeId = createChange().getChangeId(); - List<String> expected = Arrays.asList("tag1"); - assertResult(POST(changeId, "tag1", null), expected); - assertResult(POST(changeId, null, "tag1"), ImmutableList.<String>of()); - assertResult(GET(changeId), ImmutableList.<String>of()); + // Removing a single tag from a change that only has that tag returns an + // empty list. + PushOneCommit.Result r = createChange(); + addHashtags(r, "tag1"); + assertThatGet(r).containsExactly("tag1"); + removeHashtags(r, "tag1"); + assertThatGet(r).isEmpty(); - // POST removing a single tag from a change that has multiple tags - // returns a sorted list of remaining tags - expected = Arrays.asList("tag1", "tag2", "tag3"); - assertResult(POST(changeId, "tag1, tag2, tag3", null), expected); - expected = Arrays.asList("tag1", "tag3"); - assertResult(POST(changeId, null, "tag2"), expected); - assertResult(GET(changeId), expected); + // Removing a single tag from a change that has multiple tags returns a + // sorted list of remaining tags. + addHashtags(r, "tag1", "tag2", "tag3"); + removeHashtags(r, "tag2"); + assertThatGet(r).containsExactly("tag1", "tag3").inOrder(); } @Test public void testRemoveMultipleHashtags() throws Exception { - // POST removing multiple tags from a change that only has those tags - // returns an empty list - String changeId = createChange().getChangeId(); - List<String> expected = Arrays.asList("tag1", "tag2"); - assertResult(POST(changeId, "tag1, tag2", null), expected); - assertResult(POST(changeId, null, "tag1, tag2"), ImmutableList.<String>of()); - assertResult(GET(changeId), ImmutableList.<String>of()); + // Removing multiple tags from a change that only has those tags returns an + // empty list. + PushOneCommit.Result r = createChange(); + addHashtags(r, "tag1", "tag2"); + assertThatGet(r).containsExactly("tag1", "tag2").inOrder(); + removeHashtags(r, "tag1", "tag2"); + assertThatGet(r).isEmpty(); - // POST removing multiple tags from a change that has multiple changes - // returns a sorted list of remaining changes - expected = Arrays.asList("tag1", "tag2", "tag3", "tag4"); - assertResult(POST(changeId, "tag1, tag2, tag3, tag4", null), expected); - expected = Arrays.asList("tag2", "tag4"); - assertResult(POST(changeId, null, "tag1, tag3"), expected); - assertResult(GET(changeId), expected); + // Removing multiple tags from a change that has multiple tags returns a + // sorted list of remaining tags. + addHashtags(r, "tag1", "tag2", "tag3", "tag4"); + assertThatGet(r).containsExactly("tag1", "tag2", "tag3", "tag4").inOrder(); + removeHashtags(r, "tag2", "tag4"); + assertThatGet(r).containsExactly("tag1", "tag3").inOrder(); } @Test public void testRemoveNotExistingHashtag() throws Exception { - // POST removing a single hashtag from change that has no hashtags - // returns an empty list - String changeId = createChange().getChangeId(); - assertResult(POST(changeId, null, "tag1"), ImmutableList.<String>of()); - assertResult(GET(changeId), ImmutableList.<String>of()); + // Removing a single hashtag from change that has no hashtags returns an + // empty list. + PushOneCommit.Result r = createChange(); + removeHashtags(r, "tag1"); + assertThatGet(r).isEmpty(); - // POST removing a single non-existing tag from a change that only - // has one other tag returns a list of only one tag - List<String> expected = Arrays.asList("tag1"); - assertResult(POST(changeId, "tag1", null), expected); - assertResult(POST(changeId, null, "tag4"), expected); - assertResult(GET(changeId), expected); + // Removing a single non-existing tag from a change that only has one other + // tag returns a list of only one tag. + addHashtags(r, "tag1"); + removeHashtags(r, "tag4"); + assertThatGet(r).containsExactly("tag1"); - // POST removing a single non-existing tag from a change that has multiple - // tags returns a sorted list of tags without any deleted - expected = Arrays.asList("tag1", "tag2", "tag3"); - assertResult(POST(changeId, "tag1, tag2, tag3", null), expected); - assertResult(POST(changeId, null, "tag4"), expected); - assertResult(GET(changeId), expected); + // Removing a single non-existing tag from a change that has multiple tags + // returns a sorted list of tags without any deleted. + addHashtags(r, "tag1", "tag2", "tag3"); + removeHashtags(r, "tag4"); + assertThatGet(r).containsExactly("tag1", "tag2", "tag3").inOrder(); } - private RestResponse GET(String changeId) throws IOException { - return adminSession.get("/changes/" + changeId + "/hashtags/"); - } - - private RestResponse POST(String changeId, String toAdd, String toRemove) - throws IOException { + @Test + public void testAddAndRemove() throws Exception { + // Adding and remove hashtags in a single request performs correctly. + PushOneCommit.Result r = createChange(); + addHashtags(r, "tag1", "tag2"); HashtagsInput input = new HashtagsInput(); - if (toAdd != null) { - input.add = new HashSet<>( - Lists.newArrayList(Splitter.on(CharMatcher.anyOf(",")).split(toAdd))); - } - if (toRemove != null) { - input.remove = new HashSet<>( - Lists.newArrayList(Splitter.on(CharMatcher.anyOf(",")).split(toRemove))); - } - return adminSession.post("/changes/" + changeId + "/hashtags/", input); + input.add = Sets.newHashSet("tag3", "tag4"); + input.remove = Sets.newHashSet("tag1"); + gApi.changes().id(r.getChange().getId().get()).setHashtags(input); + assertThatGet(r).containsExactly("tag2", "tag3", "tag4"); + + // Adding and removing the same hashtag actually removes it. + addHashtags(r, "tag1", "tag2"); + input = new HashtagsInput(); + input.add = Sets.newHashSet("tag3", "tag4"); + input.remove = Sets.newHashSet("tag3"); + gApi.changes().id(r.getChange().getId().get()).setHashtags(input); + assertThatGet(r).containsExactly("tag1", "tag2", "tag4"); } - private static List<String> toHashtagList(RestResponse r) - throws IOException { - List<String> result = - newGson().fromJson(r.getReader(), - new TypeToken<List<String>>() {}.getType()); - return result; + private IterableSubject< + ? extends IterableSubject<?, String, Iterable<String>>, + String, Iterable<String>> + assertThatGet(PushOneCommit.Result r) throws Exception { + return assertThat(gApi.changes() + .id(r.getChange().getId().get()) + .getHashtags()); + } + + private void addHashtags(PushOneCommit.Result r, String... toAdd) + throws Exception { + HashtagsInput input = new HashtagsInput(); + input.add = Sets.newHashSet(toAdd); + gApi.changes() + .id(r.getChange().getId().get()) + .setHashtags(input); + } + + private void removeHashtags(PushOneCommit.Result r, String... toRemove) + throws Exception { + HashtagsInput input = new HashtagsInput(); + input.remove = Sets.newHashSet(toRemove); + gApi.changes() + .id(r.getChange().getId().get()) + .setHashtags(input); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java index e649415..3728a51 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
@@ -51,8 +51,8 @@ String subject = "Change subject"; String fileName = "a.txt"; PushOneCommit push = pushFactory.create( - db, admin.getIdent(), subject, fileName, content, baseChangeId); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + db, admin.getIdent(), testRepo, subject, fileName, content, baseChangeId); + PushOneCommit.Result r = push.to("refs/for/master"); r.assertOkStatus(); return r; } @@ -68,7 +68,7 @@ public void currentRevision() throws Exception { ChangeInfo c = get(changeId, CURRENT_REVISION); assertThat(c.currentRevision).isEqualTo(commitId(2)); - assertThat((Iterable<?>)c.revisions.keySet()).containsAllIn( + assertThat(c.revisions.keySet()).containsAllIn( ImmutableSet.of(commitId(2))); assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3); } @@ -78,7 +78,7 @@ ChangeInfo c = get(changeId, CURRENT_REVISION, MESSAGES); assertThat(c.revisions).hasSize(1); assertThat(c.currentRevision).isEqualTo(commitId(2)); - assertThat((Iterable<?>)c.revisions.keySet()).containsAllIn( + assertThat(c.revisions.keySet()).containsAllIn( ImmutableSet.of(commitId(2))); assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3); } @@ -87,7 +87,7 @@ public void allRevisions() throws Exception { ChangeInfo c = get(changeId, ALL_REVISIONS); assertThat(c.currentRevision).isEqualTo(commitId(2)); - assertThat((Iterable<?>)c.revisions.keySet()).containsAllIn( + assertThat(c.revisions.keySet()).containsAllIn( ImmutableSet.of(commitId(0), commitId(1), commitId(2))); assertThat(c.revisions.get(commitId(0))._number).isEqualTo(1); assertThat(c.revisions.get(commitId(1))._number).isEqualTo(2);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java index 4863c3e..c00a260 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -15,16 +15,16 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.TestProjectInput; import com.google.gerrit.extensions.client.ChangeStatus; +import com.google.gerrit.extensions.client.InheritableBoolean; import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ChangeInfo; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -39,28 +39,26 @@ @Test public void submitWithCherryPickIfFastForwardPossible() throws Exception { - Git git = createProject(); - PushOneCommit.Result change = createChange(git); + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); - assertCherryPick(git, false); + assertCherryPick(testRepo, false); assertThat(getRemoteHead().getParent(0)) .isEqualTo(change.getCommit().getParent(0)); } @Test public void submitWithCherryPick() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "b.txt", "other content"); + createChange("Change 2", "b.txt", "other content"); submit(change2.getChangeId()); - assertCherryPick(git, false); + assertCherryPick(testRepo, false); RevCommit newHead = getRemoteHead(); assertThat(newHead.getParentCount()).isEqualTo(1); assertThat(newHead.getParent(0)).isEqualTo(oldHead); @@ -70,22 +68,21 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithContentMerge() throws Exception { - Git git = createProject(); - setUseContentMerge(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n"); + createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n"); submit(change.getChangeId()); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); + createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); submit(change2.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, change.getCommitId().getName()); + testRepo.reset(change.getCommitId()); PushOneCommit.Result change3 = - createChange(git, "Change 3", "a.txt", "bbb\nccc\n"); + createChange("Change 3", "a.txt", "bbb\nccc\n"); submit(change3.getChangeId()); - assertCherryPick(git, true); + assertCherryPick(testRepo, true); RevCommit newHead = getRemoteHead(); assertThat(newHead.getParent(0)).isEqualTo(oldHead); assertApproved(change3.getChangeId()); @@ -95,18 +92,17 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithContentMerge_Conflict() throws Exception { - Git git = createProject(); - setUseContentMerge(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "other content"); + createChange("Change 2", "a.txt", "other content"); submitWithConflict(change2.getChangeId()); assertThat(getRemoteHead()).isEqualTo(oldHead); assertCurrentRevision(change2.getChangeId(), 1, change2.getCommitId()); @@ -115,19 +111,18 @@ @Test public void submitOutOfOrder() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - createChange(git, "Change 2", "b.txt", "other content"); + testRepo.reset(initialHead); + createChange("Change 2", "b.txt", "other content"); PushOneCommit.Result change3 = - createChange(git, "Change 3", "c.txt", "different content"); + createChange("Change 3", "c.txt", "different content"); submit(change3.getChangeId()); - assertCherryPick(git, false); + assertCherryPick(testRepo, false); RevCommit newHead = getRemoteHead(); assertThat(newHead.getParent(0)).isEqualTo(oldHead); assertApproved(change3.getChangeId()); @@ -138,17 +133,16 @@ @Test public void submitOutOfOrder_Conflict() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - createChange(git, "Change 2", "b.txt", "other content"); + testRepo.reset(initialHead); + createChange("Change 2", "b.txt", "other content"); PushOneCommit.Result change3 = - createChange(git, "Change 3", "b.txt", "different content"); + createChange("Change 3", "b.txt", "different content"); submitWithConflict(change3.getChangeId()); assertThat(getRemoteHead()).isEqualTo(oldHead); assertCurrentRevision(change3.getChangeId(), 1, change3.getCommitId()); @@ -157,17 +151,16 @@ @Test public void submitMultipleChanges() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c"); + testRepo.reset(initialHead); + PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change4 = createChange(git, "Change 4", "d", "d"); + testRepo.reset(initialHead); + PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); submitStatusOnly(change2.getChangeId()); submitStatusOnly(change3.getChangeId()); @@ -191,12 +184,11 @@ @Test public void submitDependentNonConflictingChangesOutOfOrder() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b"); - PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b"); + PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); assertThat(change3.getCommit().getParent(0)).isEqualTo(change2.getCommit()); // Submit succeeds; change3 is successfully cherry-picked onto head. @@ -220,12 +212,11 @@ @Test public void submitDependentConflictingChangesOutOfOrder() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b1"); - PushOneCommit.Result change3 = createChange(git, "Change 3", "b", "b2"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b1"); + PushOneCommit.Result change3 = createChange("Change 3", "b", "b2"); assertThat(change3.getCommit().getParent(0)).isEqualTo(change2.getCommit()); // Submit fails; change3 contains the delta "b1" -> "b2", which cannot be @@ -244,14 +235,13 @@ @Test public void submitSubsetOfDependentChanges() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - createChange(git, "Change 2", "b", "b"); - PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c"); - createChange(git, "Change 4", "d", "d"); - PushOneCommit.Result change5 = createChange(git, "Change 5", "e", "e"); + testRepo.reset(initialHead); + createChange("Change 2", "b", "b"); + PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); + createChange("Change 4", "d", "d"); + PushOneCommit.Result change5 = createChange("Change 5", "e", "e"); // Out of the above, only submit 3 and 5. submitStatusOnly(change3.getChangeId()); @@ -271,17 +261,16 @@ @Test public void submitChangeAfterParentFailsDueToConflict() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b1"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b1"); submit(change2.getChangeId()); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change3 = createChange(git, "Change 3", "b", "b2"); + testRepo.reset(initialHead); + PushOneCommit.Result change3 = createChange("Change 3", "b", "b2"); assertThat(change3.getCommit().getParent(0)).isEqualTo(initialHead); - PushOneCommit.Result change4 = createChange(git, "Change 3", "c", "c3"); + PushOneCommit.Result change4 = createChange("Change 3", "c", "c3"); submitStatusOnly(change3.getChangeId()); submitStatusOnly(change4.getChangeId());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java index d4e7e849..12df6fc 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -15,15 +15,16 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.extensions.client.SubmitType; +import com.google.gerrit.extensions.common.ActionInfo; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; +import java.util.Map; + public class SubmitByFastForwardIT extends AbstractSubmit { @Override @@ -33,9 +34,8 @@ @Test public void submitWithFastForward() throws Exception { - Git git = createProject(); RevCommit oldHead = getRemoteHead(); - PushOneCommit.Result change = createChange(git); + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getId()).isEqualTo(change.getCommitId()); @@ -45,16 +45,23 @@ @Test public void submitFastForwardNotPossible_Conflict() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "b.txt", "other content"); + createChange("Change 2", "b.txt", "other content"); + + approve(change2.getChangeId()); + Map<String, ActionInfo> actions = getActions(change2.getChangeId()); + + assertThat(actions).containsKey("submit"); + ActionInfo info = actions.get("submit"); + assertThat(info.enabled).isNull(); + submitWithConflict(change2.getChangeId()); assertThat(getRemoteHead()).isEqualTo(oldHead); assertSubmitter(change.getChangeId(), 1);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java index fa913d9..ebd3d3c 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
@@ -15,12 +15,10 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.extensions.client.SubmitType; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -35,9 +33,8 @@ @Test public void submitWithMergeIfFastForwardPossible() throws Exception { - Git git = createProject(); RevCommit oldHead = getRemoteHead(); - PushOneCommit.Result change = createChange(git); + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getParentCount()).isEqualTo(2); @@ -48,17 +45,16 @@ @Test public void submitMultipleChanges() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c"); + testRepo.reset(initialHead); + PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change4 = createChange(git, "Change 4", "d", "d"); + testRepo.reset(initialHead); + PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); submitStatusOnly(change2.getChangeId()); submitStatusOnly(change3.getChangeId());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java index c1ece45..95aef9e 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -1,12 +1,10 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.extensions.client.SubmitType; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -21,9 +19,8 @@ @Test public void submitWithFastForward() throws Exception { - Git git = createProject(); RevCommit oldHead = getRemoteHead(); - PushOneCommit.Result change = createChange(git); + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getId()).isEqualTo(change.getCommitId()); @@ -33,17 +30,16 @@ @Test public void submitMultipleChanges() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b"); + testRepo.reset(initialHead); + PushOneCommit.Result change2 = createChange("Change 2", "b", "b"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c"); + testRepo.reset(initialHead); + PushOneCommit.Result change3 = createChange("Change 3", "c", "c"); - checkout(git, initialHead.getId().getName()); - PushOneCommit.Result change4 = createChange(git, "Change 4", "d", "d"); + testRepo.reset(initialHead); + PushOneCommit.Result change4 = createChange("Change 4", "d", "d"); submitStatusOnly(change2.getChangeId()); submitStatusOnly(change3.getChangeId());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java index d3e8cb5..bb26a30 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
@@ -15,12 +15,12 @@ package com.google.gerrit.acceptance.rest.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.extensions.client.InheritableBoolean; import com.google.gerrit.extensions.client.SubmitType; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -32,10 +32,10 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithFastForward() throws Exception { - Git git = createProject(); RevCommit oldHead = getRemoteHead(); - PushOneCommit.Result change = createChange(git); + PushOneCommit.Result change = createChange(); submit(change.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head.getId()).isEqualTo(change.getCommitId()); @@ -46,19 +46,19 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithRebase() throws Exception { - Git git = createProject(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "b.txt", "other content"); + createChange("Change 2", "b.txt", "other content"); submit(change2.getChangeId()); - assertRebase(git, false); + assertRebase(testRepo, false); RevCommit head = getRemoteHead(); assertThat(head.getParent(0)).isEqualTo(oldHead); assertApproved(change2.getChangeId()); @@ -68,22 +68,21 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithContentMerge() throws Exception { - Git git = createProject(); - setUseContentMerge(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "aaa\nbbb\nccc\n"); + createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n"); submit(change.getChangeId()); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); + createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n"); submit(change2.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, change.getCommitId().getName()); + testRepo.reset(change.getCommitId()); PushOneCommit.Result change3 = - createChange(git, "Change 3", "a.txt", "bbb\nccc\n"); + createChange("Change 3", "a.txt", "bbb\nccc\n"); submit(change3.getChangeId()); - assertRebase(git, true); + assertRebase(testRepo, true); RevCommit head = getRemoteHead(); assertThat(head.getParent(0)).isEqualTo(oldHead); assertApproved(change3.getChangeId()); @@ -93,18 +92,17 @@ } @Test + @TestProjectInput(useContentMerge = InheritableBoolean.TRUE) public void submitWithContentMerge_Conflict() throws Exception { - Git git = createProject(); - setUseContentMerge(); RevCommit initialHead = getRemoteHead(); PushOneCommit.Result change = - createChange(git, "Change 1", "a.txt", "content"); + createChange("Change 1", "a.txt", "content"); submit(change.getChangeId()); RevCommit oldHead = getRemoteHead(); - checkout(git, initialHead.getId().getName()); + testRepo.reset(initialHead); PushOneCommit.Result change2 = - createChange(git, "Change 2", "a.txt", "other content"); + createChange("Change 2", "a.txt", "other content"); submitWithConflict(change2.getChangeId()); RevCommit head = getRemoteHead(); assertThat(head).isEqualTo(oldHead);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java index 7fee9f4..ed6740a 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -16,38 +16,42 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GerritConfig; import com.google.gerrit.acceptance.GerritConfigs; -import com.google.gerrit.acceptance.RestSession; import com.google.gerrit.acceptance.TestAccount; -import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.data.Permission; -import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.common.data.GroupDescription; +import com.google.gerrit.common.data.GroupDescriptions; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.common.SuggestedReviewerInfo; +import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.account.CreateGroupArgs; -import com.google.gerrit.server.account.PerformCreateGroup; -import com.google.gerrit.server.git.MetaDataUpdate; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gson.reflect.TypeToken; +import com.google.gerrit.server.group.CreateGroup; +import com.google.gerrit.server.group.GroupsCollection; import com.google.inject.Inject; import org.junit.Before; import org.junit.Test; -import java.io.IOException; -import java.util.Collections; +import java.util.Arrays; import java.util.List; public class SuggestReviewersIT extends AbstractDaemonTest { @Inject - private PerformCreateGroup.Factory createGroupFactory; + private CreateGroup.Factory createGroupFactory; + + @Inject + private GroupsCollection groups; private AccountGroup group1; + private AccountGroup group2; + private AccountGroup group3; + private TestAccount user1; private TestAccount user2; private TestAccount user3; @@ -55,22 +59,20 @@ @Before public void setUp() throws Exception { group1 = group("users1"); - group("users2"); - group("users3"); + group2 = group("users2"); + group3 = group("users3"); - user1 = accounts.create("user1", "user1@example.com", "First1 Last1", - "users1"); - user2 = accounts.create("user2", "user2@example.com", "First2 Last2", - "users2"); - user3 = accounts.create("user3", "user3@example.com", "First3 Last3", - "users1", "users2"); + user1 = user("user1", "First1 Last1", group1); + user2 = user("user2", "First2 Last2", group2); + user3 = user("user3", "First3 Last3", group1, group2); } @Test @GerritConfig(name = "suggest.accounts", value = "false") public void suggestReviewersNoResult1() throws Exception { String changeId = createChange().getChangeId(); - List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6); + List<SuggestedReviewerInfo> reviewers = + suggestReviewers(changeId, name("u"), 6); assertThat(reviewers).isEmpty(); } @@ -82,7 +84,8 @@ }) public void suggestReviewersNoResult2() throws Exception { String changeId = createChange().getChangeId(); - List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6); + List<SuggestedReviewerInfo> reviewers = + suggestReviewers(changeId, name("u"), 6); assertThat(reviewers).isEmpty(); } @@ -90,18 +93,20 @@ @GerritConfig(name = "suggest.from", value = "2") public void suggestReviewersNoResult3() throws Exception { String changeId = createChange().getChangeId(); - List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6); + List<SuggestedReviewerInfo> reviewers = + suggestReviewers(changeId, name("").substring(0, 1), 6); assertThat(reviewers).isEmpty(); } @Test public void suggestReviewersChange() throws Exception { String changeId = createChange().getChangeId(); - List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "u", 6); + List<SuggestedReviewerInfo> reviewers = + suggestReviewers(changeId, name("u"), 6); assertThat(reviewers).hasSize(6); - reviewers = suggestReviewers(changeId, "u", 5); + reviewers = suggestReviewers(changeId, name("u"), 5); assertThat(reviewers).hasSize(5); - reviewers = suggestReviewers(changeId, "users3", 10); + reviewers = suggestReviewers(changeId, group3.getName(), 10); assertThat(reviewers).hasSize(1); } @@ -111,26 +116,26 @@ String changeId = createChange().getChangeId(); List<SuggestedReviewerInfo> reviewers; - reviewers = suggestReviewers(changeId, "user2", 2); + reviewers = suggestReviewers(changeId, user2.username, 2); assertThat(reviewers).hasSize(1); - assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo( - "First2 Last2"); + assertThat(Iterables.getOnlyElement(reviewers).account.name) + .isEqualTo(user2.fullName); - reviewers = suggestReviewers(new RestSession(server, user1), - changeId, "user2", 2); + setApiUser(user1); + reviewers = suggestReviewers(changeId, user2.fullName, 2); assertThat(reviewers).isEmpty(); - reviewers = suggestReviewers(new RestSession(server, user2), - changeId, "user2", 2); + setApiUser(user2); + reviewers = suggestReviewers(changeId, user2.username, 2); assertThat(reviewers).hasSize(1); - assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo( - "First2 Last2"); + assertThat(Iterables.getOnlyElement(reviewers).account.name) + .isEqualTo(user2.fullName); - reviewers = suggestReviewers(new RestSession(server, user3), - changeId, "user2", 2); + setApiUser(user3); + reviewers = suggestReviewers(changeId, user2.username, 2); assertThat(reviewers).hasSize(1); - assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo( - "First2 Last2"); + assertThat(Iterables.getOnlyElement(reviewers).account.name) + .isEqualTo(user2.fullName); } @Test @@ -139,16 +144,17 @@ String changeId = createChange().getChangeId(); List<SuggestedReviewerInfo> reviewers; - reviewers = suggestReviewers(new RestSession(server, user1), - changeId, "user2", 2); + setApiUser(user1); + reviewers = suggestReviewers(changeId, user2.username, 2); assertThat(reviewers).isEmpty(); - grantCapability(GlobalCapability.VIEW_ALL_ACCOUNTS, group1); - reviewers = suggestReviewers(new RestSession(server, user1), - changeId, "user2", 2); + setApiUser(user1); // Clear cached group info. + allowGlobalCapabilities(group1.getGroupUUID(), + GlobalCapability.VIEW_ALL_ACCOUNTS); + reviewers = suggestReviewers(changeId, user2.username, 2); assertThat(reviewers).hasSize(1); - assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo( - "First2 Last2"); + assertThat(Iterables.getOnlyElement(reviewers).account.name) + .isEqualTo(user2.fullName); } @Test @@ -156,7 +162,7 @@ public void suggestReviewersMaxNbrSuggestions() throws Exception { String changeId = createChange().getChangeId(); List<SuggestedReviewerInfo> reviewers = - suggestReviewers(changeId, "user", 5); + suggestReviewers(changeId, name("user"), 5); assertThat(reviewers).hasSize(2); } @@ -193,19 +199,19 @@ reviewers = suggestReviewers(changeId, "first1 last2", 1); assertThat(reviewers).hasSize(0); - reviewers = suggestReviewers(changeId, "user", 8); - assertThat(reviewers).hasSize(7); + reviewers = suggestReviewers(changeId, name("user"), 7); + assertThat(reviewers).hasSize(6); - reviewers = suggestReviewers(changeId, "user1", 2); + reviewers = suggestReviewers(changeId, user1.username, 2); assertThat(reviewers).hasSize(1); reviewers = suggestReviewers(changeId, "example.com", 6); assertThat(reviewers).hasSize(5); - reviewers = suggestReviewers(changeId, "user1@example.com", 2); + reviewers = suggestReviewers(changeId, user1.email, 2); assertThat(reviewers).hasSize(1); - reviewers = suggestReviewers(changeId, "user1 example", 2); + reviewers = suggestReviewers(changeId, user1.username + " example", 2); assertThat(reviewers).hasSize(1); } @@ -217,60 +223,47 @@ public void suggestReviewersFullTextSearchLimitMaxMatches() throws Exception { String changeId = createChange().getChangeId(); List<SuggestedReviewerInfo> reviewers = - suggestReviewers(changeId, "user", 3); - assertThat(reviewers).hasSize(3); + suggestReviewers(changeId, name("user"), 2); + assertThat(reviewers).hasSize(2); } @Test public void suggestReviewersWithoutLimitOptionSpecified() throws Exception { String changeId = createChange().getChangeId(); - String query = "users3"; - List<SuggestedReviewerInfo> suggestedReviewerInfos = newGson().fromJson( - adminSession.get("/changes/" - + changeId - + "/suggest_reviewers?q=" - + query) - .getReader(), - new TypeToken<List<SuggestedReviewerInfo>>() {} - .getType()); + String query = user3.username; + List<SuggestedReviewerInfo> suggestedReviewerInfos = gApi.changes() + .id(changeId) + .suggestReviewers(query) + .get(); assertThat(suggestedReviewerInfos).hasSize(1); } - private List<SuggestedReviewerInfo> suggestReviewers(RestSession session, - String changeId, String query, int n) throws IOException { - return newGson().fromJson( - session.get("/changes/" - + changeId - + "/suggest_reviewers?q=" - + Url.encode(query) - + "&n=" - + n) - .getReader(), - new TypeToken<List<SuggestedReviewerInfo>>() {} - .getType()); - } - private List<SuggestedReviewerInfo> suggestReviewers(String changeId, - String query, int n) throws IOException { - return suggestReviewers(adminSession, changeId, query, n); + String query, int n) throws Exception { + return gApi.changes() + .id(changeId) + .suggestReviewers(query) + .withLimit(n) + .get(); } private AccountGroup group(String name) throws Exception { - CreateGroupArgs args = new CreateGroupArgs(); - args.setGroupName(name); - args.initialMembers = Collections.singleton(admin.getId()); - return createGroupFactory.create(args).createGroup(); + GroupInfo group = createGroupFactory.create(name(name)) + .apply(TopLevelResource.INSTANCE, null); + GroupDescription.Basic d = groups.parseInternal(Url.decode(group.id)); + return GroupDescriptions.toAccountGroup(d); } - private void grantCapability(String name, AccountGroup group) + private TestAccount user(String name, String fullName, AccountGroup... groups) throws Exception { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); - ProjectConfig config = ProjectConfig.read(md); - AccessSection s = config.getAccessSection( - AccessSection.GLOBAL_CAPABILITIES); - Permission p = s.getPermission(name, true); - p.add(new PermissionRule(config.resolve(group))); - config.commit(md); - projectCache.evict(config.getProject()); + name = name(name); + String[] groupNames = FluentIterable.from(Arrays.asList(groups)) + .transform(new Function<AccountGroup, String>() { + @Override + public String apply(AccountGroup in) { + return in.getName(); + } + }).toArray(String.class); + return accounts.create(name, name + "@example.com", fullName, groupNames); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java index 304abc8..7e68a03 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java
@@ -22,18 +22,12 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.config.ListCaches.CacheInfo; import com.google.gerrit.server.config.PostCaches; -import com.google.gerrit.server.git.MetaDataUpdate; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.group.SystemGroupBackend; -import com.google.gerrit.server.project.Util; import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; import java.util.Arrays; public class CacheOperationsIT extends AbstractDaemonTest { @@ -42,7 +36,7 @@ public void flushAll() throws Exception { RestResponse r = adminSession.get("/config/server/caches/project_list"); CacheInfo cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long) 0); + assertThat(cacheInfo.entries.mem).isGreaterThan((long) 0); r = adminSession.post("/config/server/caches/", new PostCaches.Input(FLUSH_ALL)); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); @@ -71,11 +65,11 @@ public void flush() throws Exception { RestResponse r = adminSession.get("/config/server/caches/project_list"); CacheInfo cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long)0); + assertThat(cacheInfo.entries.mem).isGreaterThan((long)0); r = adminSession.get("/config/server/caches/projects"); cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long)1); + assertThat(cacheInfo.entries.mem).isGreaterThan((long)1); r = adminSession.post("/config/server/caches/", new PostCaches.Input(FLUSH, Arrays.asList("accounts", "project_list"))); @@ -88,7 +82,7 @@ r = adminSession.get("/config/server/caches/projects"); cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long)1); + assertThat(cacheInfo.entries.mem).isGreaterThan((long)1); } @Test @@ -109,7 +103,7 @@ public void flush_UnprocessableEntity() throws Exception { RestResponse r = adminSession.get("/config/server/caches/projects"); CacheInfo cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long)0); + assertThat(cacheInfo.entries.mem).isGreaterThan((long)0); r = adminSession.post("/config/server/caches/", new PostCaches.Input(FLUSH, Arrays.asList("projects", "unprocessable"))); @@ -118,35 +112,25 @@ r = adminSession.get("/config/server/caches/projects"); cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(cacheInfo.entries.mem.longValue()).isGreaterThan((long)0); + assertThat(cacheInfo.entries.mem).isGreaterThan((long)0); } @Test public void flushWebSessions_Forbidden() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - AccountGroup.UUID registeredUsers = - SystemGroupBackend.getGroup(REGISTERED_USERS).getUUID(); - Util.allow(cfg, GlobalCapability.VIEW_CACHES, registeredUsers); - Util.allow(cfg, GlobalCapability.FLUSH_CACHES, registeredUsers); - saveProjectConfig(cfg); - - RestResponse r = userSession.post("/config/server/caches/", - new PostCaches.Input(FLUSH, Arrays.asList("projects"))); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); - - r = userSession.post("/config/server/caches/", - new PostCaches.Input(FLUSH, Arrays.asList("web_sessions"))); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); - } - - private void saveProjectConfig(ProjectConfig cfg) throws IOException { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); + allowGlobalCapabilities(REGISTERED_USERS, + GlobalCapability.FLUSH_CACHES, GlobalCapability.VIEW_CACHES); try { - cfg.commit(md); + RestResponse r = userSession.post("/config/server/caches/", + new PostCaches.Input(FLUSH, Arrays.asList("projects"))); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + r.consume(); + + r = userSession.post("/config/server/caches/", + new PostCaches.Input(FLUSH, Arrays.asList("web_sessions"))); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); } finally { - md.close(); + removeGlobalCapabilities(REGISTERED_USERS, + GlobalCapability.FLUSH_CACHES, GlobalCapability.VIEW_CACHES); } - projectCache.evict(allProjects); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java index 9f7b419..bb63928 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
@@ -20,25 +20,18 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.config.ListCaches.CacheInfo; -import com.google.gerrit.server.git.MetaDataUpdate; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.group.SystemGroupBackend; -import com.google.gerrit.server.project.Util; import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; - public class FlushCacheIT extends AbstractDaemonTest { @Test public void flushCache() throws Exception { RestResponse r = adminSession.get("/config/server/caches/groups"); CacheInfo result = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(result.entries.mem.longValue()).isGreaterThan((long)0); + assertThat(result.entries.mem).isGreaterThan((long)0); r = adminSession.post("/config/server/caches/groups/flush"); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); @@ -75,28 +68,18 @@ @Test public void flushWebSessionsCache_Forbidden() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - AccountGroup.UUID registeredUsers = - SystemGroupBackend.getGroup(REGISTERED_USERS).getUUID(); - Util.allow(cfg, GlobalCapability.VIEW_CACHES, registeredUsers); - Util.allow(cfg, GlobalCapability.FLUSH_CACHES, registeredUsers); - saveProjectConfig(cfg); - - RestResponse r = userSession.post("/config/server/caches/accounts/flush"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); - - r = userSession.post("/config/server/caches/web_sessions/flush"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); - } - - private void saveProjectConfig(ProjectConfig cfg) throws IOException { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); + allowGlobalCapabilities(REGISTERED_USERS, + GlobalCapability.VIEW_CACHES, GlobalCapability.FLUSH_CACHES); try { - cfg.commit(md); + RestResponse r = userSession.post("/config/server/caches/accounts/flush"); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + r.consume(); + + r = userSession.post("/config/server/caches/web_sessions/flush"); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); } finally { - md.close(); + removeGlobalCapabilities(REGISTERED_USERS, + GlobalCapability.VIEW_CACHES, GlobalCapability.FLUSH_CACHES); } - projectCache.evict(allProjects); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/GetCacheIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/GetCacheIT.java index f59752c..02a1b73 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/GetCacheIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/GetCacheIT.java
@@ -34,7 +34,7 @@ assertThat(result.name).isEqualTo("accounts"); assertThat(result.type).isEqualTo(CacheType.MEM); - assertThat(result.entries.mem.longValue()).isEqualTo(1); + assertThat(result.entries.mem).isAtLeast(1L); assertThat(result.averageGet).isNotNull(); assertThat(result.averageGet).endsWith("s"); assertThat(result.entries.disk).isNull(); @@ -47,7 +47,7 @@ r = adminSession.get("/config/server/caches/accounts"); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); result = newGson().fromJson(r.getReader(), CacheInfo.class); - assertThat(result.entries.mem.longValue()).isEqualTo(2); + assertThat(result.entries.mem).isEqualTo(2); } @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ListCachesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ListCachesIT.java index 0d0a94e..fb89e1b 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ListCachesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ListCachesIT.java
@@ -45,7 +45,7 @@ assertThat(result).containsKey("accounts"); CacheInfo accountsCacheInfo = result.get("accounts"); assertThat(accountsCacheInfo.type).isEqualTo(CacheType.MEM); - assertThat(accountsCacheInfo.entries.mem.longValue()).isEqualTo(1); + assertThat(accountsCacheInfo.entries.mem).isAtLeast(1L); assertThat(accountsCacheInfo.averageGet).isNotNull(); assertThat(accountsCacheInfo.averageGet).endsWith("s"); assertThat(accountsCacheInfo.entries.disk).isNull(); @@ -59,7 +59,7 @@ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); result = newGson().fromJson(r.getReader(), new TypeToken<Map<String, CacheInfo>>() {}.getType()); - assertThat(result.get("accounts").entries.mem.longValue()).isEqualTo(2); + assertThat(result.get("accounts").entries.mem).isEqualTo(2); } @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/AddRemoveGroupMembersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/AddRemoveGroupMembersIT.java deleted file mode 100644 index b9778b8..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/AddRemoveGroupMembersIT.java +++ /dev/null
@@ -1,225 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfo; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroupInfo; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.TestAccount; -import com.google.gerrit.extensions.common.AccountInfo; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.AccountGroupById; -import com.google.gerrit.reviewdb.client.AccountGroupMember; -import com.google.gerrit.server.group.AddIncludedGroups; -import com.google.gerrit.server.group.AddMembers; -import com.google.gerrit.server.group.CreateGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; -import com.google.gson.reflect.TypeToken; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.ResultSet; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class AddRemoveGroupMembersIT extends AbstractDaemonTest { - @Test - public void addToNonExistingGroup_NotFound() throws Exception { - assertThat(PUT("/groups/non-existing/members/admin").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); - } - - @Test - public void removeFromNonExistingGroup_NotFound() throws Exception { - assertThat(DELETE("/groups/non-existing/members/admin")) - .isEqualTo(HttpStatus.SC_NOT_FOUND); - } - - @Test - public void addRemoveMember() throws Exception { - RestResponse r = PUT("/groups/Administrators/members/user"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - AccountInfo ai = newGson().fromJson(r.getReader(), AccountInfo.class); - assertAccountInfo(user, ai); - assertMembers("Administrators", admin, user); - r.consume(); - - assertThat(DELETE("/groups/Administrators/members/user")) - .isEqualTo(HttpStatus.SC_NO_CONTENT); - assertMembers("Administrators", admin); - } - - @Test - public void addExistingMember_OK() throws Exception { - assertThat(PUT("/groups/Administrators/members/admin").getStatusCode()) - .isEqualTo(HttpStatus.SC_OK); - } - - @Test - public void addMultipleMembers() throws Exception { - group("users"); - TestAccount u1 = accounts.create("u1", "u1@example.com", "Full Name 1"); - TestAccount u2 = accounts.create("u2", "u2@example.com", "Full Name 2"); - List<String> members = Lists.newLinkedList(); - members.add(u1.username); - members.add(u2.username); - AddMembers.Input input = AddMembers.Input.fromMembers(members); - RestResponse r = POST("/groups/users/members", input); - List<AccountInfo> ai = newGson().fromJson(r.getReader(), - new TypeToken<List<AccountInfo>>() {}.getType()); - assertMembers(ai, u1, u2); - } - - @Test - public void includeRemoveGroup() throws Exception { - group("newGroup"); - RestResponse r = PUT("/groups/Administrators/groups/newGroup"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - GroupInfo i = newGson().fromJson(r.getReader(), GroupInfo.class); - r.consume(); - assertGroupInfo(groupCache.get(new AccountGroup.NameKey("newGroup")), i); - assertIncludes("Administrators", "newGroup"); - - assertThat(DELETE("/groups/Administrators/groups/newGroup")) - .isEqualTo(HttpStatus.SC_NO_CONTENT); - assertNoIncludes("Administrators"); - } - - @Test - public void includeExistingGroup_OK() throws Exception { - group("newGroup"); - PUT("/groups/Administrators/groups/newGroup").consume(); - assertThat(PUT("/groups/Administrators/groups/newGroup").getStatusCode()) - .isEqualTo(HttpStatus.SC_OK); - } - - @Test - public void addMultipleIncludes() throws Exception { - group("newGroup1"); - group("newGroup2"); - List<String> groups = Lists.newLinkedList(); - groups.add("newGroup1"); - groups.add("newGroup2"); - AddIncludedGroups.Input input = AddIncludedGroups.Input.fromGroups(groups); - RestResponse r = POST("/groups/Administrators/groups", input); - List<GroupInfo> gi = newGson().fromJson(r.getReader(), - new TypeToken<List<GroupInfo>>() {}.getType()); - assertIncludes(gi, "newGroup1", "newGroup2"); - } - - private RestResponse PUT(String endpoint) throws IOException { - return adminSession.put(endpoint); - } - - private int DELETE(String endpoint) throws IOException { - RestResponse r = adminSession.delete(endpoint); - r.consume(); - return r.getStatusCode(); - } - - private RestResponse POST(String endPoint, AddMembers.Input mi) - throws IOException { - return adminSession.post(endPoint, mi); - } - - private RestResponse POST(String endPoint, AddIncludedGroups.Input gi) - throws IOException { - return adminSession.post(endPoint, gi); - } - - private void group(String name) throws IOException { - CreateGroup.Input in = new CreateGroup.Input(); - adminSession.put("/groups/" + name, in).consume(); - } - - private void assertMembers(String group, TestAccount... members) - throws OrmException { - AccountGroup g = groupCache.get(new AccountGroup.NameKey(group)); - Set<Account.Id> ids = Sets.newHashSet(); - ResultSet<AccountGroupMember> all = - db.accountGroupMembers().byGroup(g.getId()); - for (AccountGroupMember m : all) { - ids.add(m.getAccountId()); - } - assertThat((Iterable<?>)ids).hasSize(members.length); - for (TestAccount a : members) { - assertThat((Iterable<?>)ids).contains(a.id); - } - } - - private void assertMembers(List<AccountInfo> ai, TestAccount... members) { - Map<Integer, AccountInfo> infoById = Maps.newHashMap(); - for (AccountInfo i : ai) { - infoById.put(i._accountId, i); - } - - for (TestAccount a : members) { - AccountInfo i = infoById.get(a.id.get()); - assertThat(i).isNotNull(); - assertAccountInfo(a, i); - } - assertThat(ai).hasSize(members.length); - } - - private void assertIncludes(String group, String... includes) - throws OrmException { - AccountGroup g = groupCache.get(new AccountGroup.NameKey(group)); - Set<AccountGroup.UUID> ids = Sets.newHashSet(); - ResultSet<AccountGroupById> all = - db.accountGroupById().byGroup(g.getId()); - for (AccountGroupById m : all) { - ids.add(m.getIncludeUUID()); - } - assertThat((Iterable<?>)ids).hasSize(includes.length); - for (String i : includes) { - AccountGroup.UUID id = groupCache.get( - new AccountGroup.NameKey(i)).getGroupUUID(); - assertThat((Iterable<?>)ids).contains(id); - } - } - - private void assertIncludes(List<GroupInfo> gi, String... includes) { - Map<String, GroupInfo> groupsByName = Maps.newHashMap(); - for (GroupInfo i : gi) { - groupsByName.put(i.name, i); - } - - for (String name : includes) { - GroupInfo i = groupsByName.get(name); - assertThat(i).isNotNull(); - assertGroupInfo(groupCache.get(new AccountGroup.NameKey(name)), i); - } - assertThat(gi).hasSize(includes.length); - } - - private void assertNoIncludes(String group) throws OrmException { - AccountGroup g = groupCache.get(new AccountGroup.NameKey(group)); - Iterator<AccountGroupById> it = - db.accountGroupById().byGroup(g.getId()).iterator(); - assertThat(it.hasNext()).isFalse(); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/CreateGroupIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/CreateGroupIT.java deleted file mode 100644 index 7de450b..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/CreateGroupIT.java +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroupInfo; - -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.RestSession; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.CreateGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -public class CreateGroupIT extends AbstractDaemonTest { - @Test - public void testCreateGroup() throws Exception { - final String newGroupName = "newGroup"; - RestResponse r = adminSession.put("/groups/" + newGroupName); - GroupInfo g = newGson().fromJson(r.getReader(), GroupInfo.class); - assertThat(g.name).isEqualTo(newGroupName); - AccountGroup group = groupCache.get(new AccountGroup.NameKey(newGroupName)); - assertThat(group).isNotNull(); - assertGroupInfo(group, g); - } - - @Test - public void testCreateGroupWithProperties() throws Exception { - final String newGroupName = "newGroup"; - CreateGroup.Input in = new CreateGroup.Input(); - in.description = "Test description"; - in.visibleToAll = true; - in.ownerId = groupCache.get(new AccountGroup.NameKey("Administrators")).getGroupUUID().get(); - RestResponse r = adminSession.put("/groups/" + newGroupName, in); - GroupInfo g = newGson().fromJson(r.getReader(), GroupInfo.class); - assertThat(g.name).isEqualTo(newGroupName); - AccountGroup group = groupCache.get(new AccountGroup.NameKey(newGroupName)); - assertThat(group.getDescription()).isEqualTo(in.description); - assertThat(group.isVisibleToAll()).isEqualTo(in.visibleToAll); - assertThat(group.getOwnerGroupUUID().get()).isEqualTo(in.ownerId); - } - - @Test - public void testCreateGroupWithoutCapability_Forbidden() throws Exception { - RestResponse r = (new RestSession(server, user)).put("/groups/newGroup"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); - } - - @Test - public void testCreateGroupWhenGroupAlreadyExists_Conflict() - throws Exception { - RestResponse r = adminSession.put("/groups/Administrators"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/DefaultGroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/DefaultGroupsIT.java deleted file mode 100644 index 8365d79..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/DefaultGroupsIT.java +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assert_; - -import com.google.common.collect.Sets; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.RestSession; -import com.google.gerrit.acceptance.SshSession; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; -import com.google.gson.reflect.TypeToken; - -import org.junit.Test; - -import java.util.Map; -import java.util.Set; - -/** - * An example test that tests presence of default groups in a newly initialized - * review site. - * - * The test shows how to perform these checks via SSH, REST or using Gerrit - * internals. - */ -public class DefaultGroupsIT extends AbstractDaemonTest { - - @Test - public void defaultGroupsCreated_ssh() throws Exception { - SshSession session = new SshSession(server, admin); - String result = session.exec("gerrit ls-groups"); - assert_().withFailureMessage(session.getError()) - .that(session.hasError()).isFalse(); - assertThat(result).contains("Administrators"); - assertThat(result).contains("Non-Interactive Users"); - session.close(); - } - - @Test - public void defaultGroupsCreated_rest() throws Exception { - RestSession session = new RestSession(server, admin); - RestResponse r = session.get("/groups/"); - Map<String, GroupInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<Map<String, GroupInfo>>() {}.getType()); - Set<String> names = result.keySet(); - assertThat((Iterable<?>)names).contains("Administrators"); - assertThat((Iterable<?>)names).contains("Non-Interactive Users"); - } - - @Test - public void defaultGroupsCreated_internals() throws Exception { - Set<String> names = Sets.newHashSet(); - for (AccountGroup g : db.accountGroups().all()) { - names.add(g.getName()); - } - assertThat((Iterable<?>)names).contains("Administrators"); - assertThat((Iterable<?>)names).contains("Non-Interactive Users"); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GetGroupIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GetGroupIT.java deleted file mode 100644 index 8dfb5ff..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GetGroupIT.java +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroupInfo; - -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; - -import org.junit.Test; - -import java.io.IOException; - -public class GetGroupIT extends AbstractDaemonTest { - @Test - public void testGetGroup() throws Exception { - AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators")); - - // by UUID - testGetGroup("/groups/" + adminGroup.getGroupUUID().get(), adminGroup); - - // by name - testGetGroup("/groups/" + adminGroup.getName(), adminGroup); - - // by legacy numeric ID - testGetGroup("/groups/" + adminGroup.getId().get(), adminGroup); - } - - private void testGetGroup(String url, AccountGroup expectedGroup) - throws IOException { - RestResponse r = adminSession.get(url); - GroupInfo group = newGson().fromJson(r.getReader(), GroupInfo.class); - assertGroupInfo(expectedGroup, group); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupPropertiesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupPropertiesIT.java deleted file mode 100644 index e9dd776..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/GroupPropertiesIT.java +++ /dev/null
@@ -1,186 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroupInfo; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.toBoolean; - -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.extensions.restapi.Url; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; -import com.google.gerrit.server.group.GroupOptionsInfo; -import com.google.gerrit.server.group.PutDescription; -import com.google.gerrit.server.group.PutName; -import com.google.gerrit.server.group.PutOptions; -import com.google.gerrit.server.group.PutOwner; -import com.google.gerrit.server.group.SystemGroupBackend; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -public class GroupPropertiesIT extends AbstractDaemonTest { - @Test - public void testGroupName() throws Exception { - AccountGroup.NameKey adminGroupName = new AccountGroup.NameKey("Administrators"); - String url = "/groups/" + groupCache.get(adminGroupName).getGroupUUID().get() + "/name"; - - // get name - RestResponse r = adminSession.get(url); - String name = newGson().fromJson(r.getReader(), String.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(name).isEqualTo("Administrators"); - r.consume(); - - // set name with name conflict - String newGroupName = "newGroup"; - r = adminSession.put("/groups/" + newGroupName); - r.consume(); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - PutName.Input in = new PutName.Input(); - in.name = newGroupName; - r = adminSession.put(url, in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); - r.consume(); - - // set name to same name - in = new PutName.Input(); - in.name = "Administrators"; - r = adminSession.put(url, in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - r.consume(); - - // rename - in = new PutName.Input(); - in.name = "Admins"; - r = adminSession.put(url, in); - String newName = newGson().fromJson(r.getReader(), String.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(groupCache.get(new AccountGroup.NameKey(in.name))).isNotNull(); - assertThat(groupCache.get(adminGroupName)).isNull(); - assertThat(newName).isEqualTo(in.name); - r.consume(); - } - - @Test - public void testGroupDescription() throws Exception { - AccountGroup.NameKey adminGroupName = new AccountGroup.NameKey("Administrators"); - AccountGroup adminGroup = groupCache.get(adminGroupName); - String url = "/groups/" + adminGroup.getGroupUUID().get() + "/description"; - - // get description - RestResponse r = adminSession.get(url); - String description = newGson().fromJson(r.getReader(), String.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(description).isEqualTo(adminGroup.getDescription()); - r.consume(); - - // set description - PutDescription.Input in = new PutDescription.Input(); - in.description = "All users that can administrate the Gerrit Server."; - r = adminSession.put(url, in); - String newDescription = newGson().fromJson(r.getReader(), String.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(newDescription).isEqualTo(in.description); - adminGroup = groupCache.get(adminGroupName); - assertThat(adminGroup.getDescription()).isEqualTo(in.description); - r.consume(); - - // delete description - r = adminSession.delete(url); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - adminGroup = groupCache.get(adminGroupName); - assertThat(adminGroup.getDescription()).isNull(); - - // set description to empty string - in = new PutDescription.Input(); - in.description = ""; - r = adminSession.put(url, in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - adminGroup = groupCache.get(adminGroupName); - assertThat(adminGroup.getDescription()).isNull(); - } - - @Test - public void testGroupOptions() throws Exception { - AccountGroup.NameKey adminGroupName = new AccountGroup.NameKey("Administrators"); - AccountGroup adminGroup = groupCache.get(adminGroupName); - String url = "/groups/" + adminGroup.getGroupUUID().get() + "/options"; - - // get options - RestResponse r = adminSession.get(url); - GroupOptionsInfo options = newGson().fromJson(r.getReader(), GroupOptionsInfo.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(toBoolean(options.visibleToAll)).isEqualTo(adminGroup.isVisibleToAll()); - r.consume(); - - // set options - PutOptions.Input in = new PutOptions.Input(); - in.visibleToAll = !adminGroup.isVisibleToAll(); - r = adminSession.put(url, in); - GroupOptionsInfo newOptions = newGson().fromJson(r.getReader(), GroupOptionsInfo.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(toBoolean(newOptions.visibleToAll)).isEqualTo(in.visibleToAll); - adminGroup = groupCache.get(adminGroupName); - assertThat(adminGroup.isVisibleToAll()).isEqualTo(in.visibleToAll); - r.consume(); - } - - @Test - public void testGroupOwner() throws Exception { - AccountGroup.NameKey adminGroupName = new AccountGroup.NameKey("Administrators"); - AccountGroup adminGroup = groupCache.get(adminGroupName); - String url = "/groups/" + adminGroup.getGroupUUID().get() + "/owner"; - - // get owner - RestResponse r = adminSession.get(url); - GroupInfo options = newGson().fromJson(r.getReader(), GroupInfo.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertGroupInfo(groupCache.get(adminGroup.getOwnerGroupUUID()), options); - r.consume(); - - // set owner by name - PutOwner.Input in = new PutOwner.Input(); - in.owner = "Registered Users"; - r = adminSession.put(url, in); - GroupInfo newOwner = newGson().fromJson(r.getReader(), GroupInfo.class); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(newOwner.name).isEqualTo(in.owner); - assertThat(newOwner.name).isEqualTo( - SystemGroupBackend.getGroup(SystemGroupBackend.REGISTERED_USERS).getName()); - assertThat(SystemGroupBackend.REGISTERED_USERS.get()) - .isEqualTo(Url.decode(newOwner.id)); - r.consume(); - - // set owner by UUID - in = new PutOwner.Input(); - in.owner = adminGroup.getGroupUUID().get(); - r = adminSession.put(url, in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - adminGroup = groupCache.get(adminGroupName); - assertThat(groupCache.get(adminGroup.getOwnerGroupUUID()).getGroupUUID().get()) - .isEqualTo(in.owner); - r.consume(); - - // set non existing owner - in = new PutOwner.Input(); - in.owner = "Non-Existing Group"; - r = adminSession.put(url, in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_UNPROCESSABLE_ENTITY); - r.consume(); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupIncludesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupIncludesIT.java deleted file mode 100644 index 5dc49c6..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupIncludesIT.java +++ /dev/null
@@ -1,106 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.server.group.CreateGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; -import com.google.gson.reflect.TypeToken; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; - -public class ListGroupIncludesIT extends AbstractDaemonTest { - - @Test - public void listNonExistingGroupIncludes_NotFound() throws Exception { - assertThat(adminSession.get("/groups/non-existing/groups/").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); - } - - @Test - public void listEmptyGroupIncludes() throws Exception { - assertThat(GET("/groups/Administrators/groups/")).isEmpty(); - } - - @Test - public void listNonEmptyGroupIncludes() throws Exception { - group("gx", "Administrators"); - group("gy", "Administrators"); - PUT("/groups/Administrators/groups/gx"); - PUT("/groups/Administrators/groups/gy"); - - assertIncludes(GET("/groups/Administrators/groups/"), "gx", "gy"); - } - - @Test - public void listOneIncludeMember() throws Exception { - group("gx", "Administrators"); - group("gy", "Administrators"); - PUT("/groups/Administrators/groups/gx"); - PUT("/groups/Administrators/groups/gy"); - - assertThat(GET_ONE("/groups/Administrators/groups/gx").name).isEqualTo("gx"); - } - - private List<GroupInfo> GET(String endpoint) throws IOException { - RestResponse r = adminSession.get(endpoint); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), - new TypeToken<List<GroupInfo>>() {}.getType()); - } - - private GroupInfo GET_ONE(String endpoint) throws IOException { - RestResponse r = adminSession.get(endpoint); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), GroupInfo.class); - } - - private void PUT(String endpoint) throws IOException { - adminSession.put(endpoint).consume(); - } - - private void group(String name, String ownerGroup) throws IOException { - CreateGroup.Input in = new CreateGroup.Input(); - in.ownerId = ownerGroup; - adminSession.put("/groups/" + name, in).consume(); - } - - private void assertIncludes(List<GroupInfo> includes, String name, - String... names) { - Collection<String> includeNames = Collections2.transform(includes, - new Function<GroupInfo, String>() { - @Override - public String apply(@Nullable GroupInfo info) { - return info.name; - } - }); - assertThat((Iterable<?>)includeNames).contains(name); - for (String n : names) { - assertThat((Iterable<?>)includeNames).contains(n); - } - assertThat(includes).hasSize(names.length + 1); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupMembersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupMembersIT.java deleted file mode 100644 index 80fb960..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupMembersIT.java +++ /dev/null
@@ -1,118 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.extensions.common.AccountInfo; -import com.google.gerrit.server.group.CreateGroup; -import com.google.gson.reflect.TypeToken; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; - -public class ListGroupMembersIT extends AbstractDaemonTest { - - @Test - public void listNonExistingGroupMembers_NotFound() throws Exception { - assertThat(adminSession.get("/groups/non-existing/members/").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); - } - - @Test - public void listEmptyGroupMembers() throws Exception { - group("empty", "Administrators"); - assertThat(GET("/groups/empty/members/")).isEmpty(); - } - - @Test - public void listNonEmptyGroupMembers() throws Exception { - assertMembers(GET("/groups/Administrators/members/"), admin.fullName); - - accounts.create("admin2", "Administrators"); - assertMembers(GET("/groups/Administrators/members/"), - admin.fullName, "admin2"); - } - - @Test - public void listOneGroupMember() throws Exception { - assertThat(GET_ONE("/groups/Administrators/members/admin").name) - .isEqualTo(admin.fullName); - } - - @Test - public void listGroupMembersRecursively() throws Exception { - group("gx", "Administrators"); - accounts.create("ux", "gx"); - - group("gy", "Administrators"); - accounts.create("uy", "gy"); - - PUT("/groups/Administrators/groups/gx"); - PUT("/groups/gx/groups/gy"); - assertMembers(GET("/groups/Administrators/members/?recursive"), - admin.fullName, "ux", "uy"); - } - - private List<AccountInfo> GET(String endpoint) throws IOException { - RestResponse r = adminSession.get(endpoint); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), - new TypeToken<List<AccountInfo>>() {}.getType()); - } - - private AccountInfo GET_ONE(String endpoint) throws IOException { - RestResponse r = adminSession.get(endpoint); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), AccountInfo.class); - } - - private void PUT(String endpoint) throws IOException { - adminSession.put(endpoint).consume(); - } - - private void group(String name, String ownerGroup) - throws IOException { - CreateGroup.Input in = new CreateGroup.Input(); - in.ownerId = ownerGroup; - adminSession.put("/groups/" + name, in).consume(); - } - - private void assertMembers(List<AccountInfo> members, String name, - String... names) { - Collection<String> memberNames = Collections2.transform(members, - new Function<AccountInfo, String>() { - @Override - public String apply(@Nullable AccountInfo info) { - return info.name; - } - }); - - assertThat((Iterable<?>)memberNames).contains(name); - for (String n : names) { - assertThat((Iterable<?>)memberNames).contains(n); - } - assertThat(members).hasSize(names.length + 1); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupsIT.java deleted file mode 100644 index 763c36a..0000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/group/ListGroupsIT.java +++ /dev/null
@@ -1,93 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.acceptance.rest.group; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroupInfo; -import static com.google.gerrit.acceptance.rest.group.GroupAssert.assertGroups; - -import com.google.common.base.Function; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.group.CreateGroup; -import com.google.gerrit.server.group.GroupJson.GroupInfo; -import com.google.gson.reflect.TypeToken; - -import org.apache.http.HttpStatus; -import org.junit.Test; - -import java.util.Map; -import java.util.Set; - -public class ListGroupsIT extends AbstractDaemonTest { - @Test - public void testListAllGroups() throws Exception { - Iterable<String> expectedGroups = Iterables.transform(groupCache.all(), - new Function<AccountGroup, String>() { - @Override - @Nullable - public String apply(@Nullable AccountGroup group) { - return group.getName(); - } - }); - RestResponse r = adminSession.get("/groups/"); - Map<String, GroupInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<Map<String, GroupInfo>>() {}.getType()); - assertGroups(expectedGroups, result.keySet()); - } - - @Test - public void testOnlyVisibleGroupsReturned() throws Exception { - String newGroupName = "newGroup"; - CreateGroup.Input in = new CreateGroup.Input(); - in.description = "a hidden group"; - in.visibleToAll = false; - in.ownerId = groupCache.get(new AccountGroup.NameKey("Administrators")) - .getGroupUUID().get(); - adminSession.put("/groups/" + newGroupName, in).consume(); - - Set<String> expectedGroups = Sets.newHashSet(newGroupName); - RestResponse r = userSession.get("/groups/"); - Map<String, GroupInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<Map<String, GroupInfo>>() {}.getType()); - assertThat(result).isEmpty(); - - r = adminSession.put( - String.format("/groups/%s/members/%s", newGroupName, user.username)); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - - r = userSession.get("/groups/"); - result = newGson().fromJson(r.getReader(), - new TypeToken<Map<String, GroupInfo>>() {}.getType()); - assertGroups(expectedGroups, result.keySet()); - } - - @Test - public void testAllGroupInfoFieldsSetCorrectly() throws Exception { - AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators")); - RestResponse r = adminSession.get("/groups/?q=" + adminGroup.getName()); - Map<String, GroupInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<Map<String, GroupInfo>>() {}.getType()); - GroupInfo adminGroupInfo = result.get(adminGroup.getName()); - assertGroupInfo(adminGroup, adminGroupInfo); - } -}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BUCK index 1efaa60..d4365c5 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BUCK +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BUCK
@@ -18,6 +18,7 @@ '//lib:guava', '//lib:junit', '//lib:truth', + '//gerrit-extension-api:api', '//gerrit-server:server', ], )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BanCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BanCommitIT.java index ceed9b6..682059c 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BanCommitIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BanCommitIT.java
@@ -15,18 +15,16 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.add; -import static com.google.gerrit.acceptance.GitUtil.createCommit; import static com.google.gerrit.acceptance.GitUtil.pushHead; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GitUtil.Commit; import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.server.project.BanCommit; import com.google.gerrit.server.project.BanCommit.BanResultInfo; import org.apache.http.HttpStatus; +import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.PushResult; import org.junit.Test; @@ -34,20 +32,20 @@ @Test public void banCommit() throws Exception { - add(git, "a.txt", "some content"); - Commit c = createCommit(git, admin.getIdent(), "subject"); + RevCommit c = commitBuilder() + .add("a.txt", "some content") + .create(); RestResponse r = adminSession.put("/projects/" + project.get() + "/ban/", - BanCommit.Input.fromCommits(c.getCommit().getName())); + BanCommit.Input.fromCommits(c.name())); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); BanResultInfo info = newGson().fromJson(r.getReader(), BanResultInfo.class); - assertThat(Iterables.getOnlyElement(info.newlyBanned)) - .isEqualTo(c.getCommit().getName()); + assertThat(Iterables.getOnlyElement(info.newlyBanned)).isEqualTo(c.name()); assertThat(info.alreadyBanned).isNull(); assertThat(info.ignored).isNull(); - PushResult pushResult = pushHead(git, "refs/heads/master", false); + PushResult pushResult = pushHead(testRepo, "refs/heads/master", false); assertThat(pushResult.getRemoteUpdate("refs/heads/master").getMessage()) .startsWith("contains banned commit"); }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BranchAssert.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BranchAssert.java index c706e17..c860bf0 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BranchAssert.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/BranchAssert.java
@@ -16,39 +16,44 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.common.base.Predicate; +import com.google.common.base.Function; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.gerrit.server.project.ListBranches.BranchInfo; +import com.google.gerrit.extensions.api.projects.BranchInfo; import java.util.List; public class BranchAssert { - public static void assertBranches(List<BranchInfo> expectedBranches, List<BranchInfo> actualBranches) { - List<BranchInfo> missingBranches = Lists.newArrayList(actualBranches); - for (final BranchInfo b : expectedBranches) { - BranchInfo info = - Iterables.find(actualBranches, new Predicate<BranchInfo>() { - @Override - public boolean apply(BranchInfo info) { - return info.ref.equals(b.ref); - } - }, null); - assertThat(info).named("branch " + b.ref).isNotNull(); - assertBranchInfo(b, info); - missingBranches.remove(info); + assertRefNames(refs(expectedBranches), actualBranches); + for (int i = 0; i < expectedBranches.size(); i++) { + assertBranchInfo(expectedBranches.get(i), actualBranches.get(i)); } - assertThat(missingBranches).named("" + missingBranches).isEmpty(); + } + + public static void assertRefNames(Iterable<String> expectedRefs, + Iterable<BranchInfo> actualBranches) { + Iterable<String> actualNames = refs(actualBranches); + assertThat(actualNames).containsExactlyElementsIn(expectedRefs).inOrder(); } public static void assertBranchInfo(BranchInfo expected, BranchInfo actual) { assertThat(actual.ref).isEqualTo(expected.ref); if (expected.revision != null) { - assertThat(actual.revision).isEqualTo(expected.revision); + assertThat(actual.revision).named("revision of " + actual.ref) + .isEqualTo(expected.revision); } - assertThat(toBoolean(actual.canDelete)).isEqualTo(expected.canDelete); + assertThat(toBoolean(actual.canDelete)).named("can delete " + actual.ref) + .isEqualTo(toBoolean(expected.canDelete)); + } + + private static Iterable<String> refs(Iterable<BranchInfo> infos) { + return Iterables.transform(infos, new Function<BranchInfo, String>() { + @Override + public String apply(BranchInfo in) { + return in.ref; + } + }); } private static boolean toBoolean(Boolean b) {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java index 3cadf66..25a226d 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -18,17 +18,25 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.server.project.Util.block; +import static org.junit.Assert.fail; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.common.data.Permission; +import com.google.gerrit.extensions.api.projects.BranchApi; +import com.google.gerrit.extensions.api.projects.BranchInfo; +import com.google.gerrit.extensions.api.projects.BranchInput; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.server.git.ProjectConfig; -import org.apache.http.HttpStatus; +import org.eclipse.jgit.lib.Constants; import org.junit.Before; import org.junit.Test; +@NoHttpd public class CreateBranchIT extends AbstractDaemonTest { private Branch.NameKey branch; @@ -39,65 +47,32 @@ @Test public void createBranch_Forbidden() throws Exception { - RestResponse r = - userSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); + setApiUser(user); + assertCreateFails(AuthException.class); } @Test public void createBranchByAdmin() throws Exception { - RestResponse r = - adminSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - r.consume(); - - r = adminSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + assertCreateSucceeds(); } @Test public void branchAlreadyExists_Conflict() throws Exception { - RestResponse r = - adminSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - r.consume(); - - r = adminSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); + assertCreateSucceeds(); + assertCreateFails(ResourceConflictException.class); } @Test public void createBranchByProjectOwner() throws Exception { grantOwner(); - - RestResponse r = - userSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - r.consume(); - - r = adminSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + setApiUser(user); + assertCreateSucceeds(); } @Test public void createBranchByAdminCreateReferenceBlocked() throws Exception { blockCreateReference(); - RestResponse r = - adminSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - r.consume(); - - r = adminSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + assertCreateSucceeds(); } @Test @@ -105,19 +80,39 @@ throws Exception { grantOwner(); blockCreateReference(); - RestResponse r = - userSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); + setApiUser(user); + assertCreateFails(AuthException.class); } private void blockCreateReference() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); block(cfg, Permission.CREATE, ANONYMOUS_USERS, "refs/*"); - saveProjectConfig(allProjects, cfg); + saveProjectConfig(project, cfg); } private void grantOwner() throws Exception { allow(Permission.OWNER, REGISTERED_USERS, "refs/*"); } + + private BranchApi branch() throws Exception { + return gApi.projects() + .name(branch.getParentKey().get()) + .branch(branch.get()); + } + + private void assertCreateSucceeds() throws Exception { + BranchInfo created = branch().create(new BranchInput()).get(); + assertThat(created.ref) + .isEqualTo(Constants.R_HEADS + branch.getShortName()); + } + + private void assertCreateFails(Class<? extends RestApiException> errType) + throws Exception { + try { + branch().create(new BranchInput()); + fail("Expected " + errType.getSimpleName()); + } catch (RestApiException expected) { + assertThat(expected).isInstanceOf(errType); + } + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java index 68c9a5e..6347874 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -17,15 +17,23 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectInfo; import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectOwners; +import static org.junit.Assert.fail; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.common.net.HttpHeaders; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.UseLocalDisk; import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.client.InheritableBoolean; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; @@ -33,6 +41,7 @@ import com.google.gerrit.server.project.ProjectState; import org.apache.http.HttpStatus; +import org.apache.http.message.BasicHeader; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; @@ -47,30 +56,8 @@ public class CreateProjectIT extends AbstractDaemonTest { @Test - public void testCreateProjectApi() throws Exception { - final String newProjectName = "newProject"; - ProjectInfo p = gApi.projects().name(newProjectName).create().get(); - assertThat(p.name).isEqualTo(newProjectName); - ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName)); - assertThat(projectState).isNotNull(); - assertProjectInfo(projectState.getProject(), p); - assertHead(newProjectName, "refs/heads/master"); - } - - @Test - public void testCreateProjectApiWithGitSuffix() throws Exception { - final String newProjectName = "newProject"; - ProjectInfo p = gApi.projects().name(newProjectName + ".git").create().get(); - assertThat(p.name).isEqualTo(newProjectName); - ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName)); - assertThat(projectState).isNotNull(); - assertProjectInfo(projectState.getProject(), p); - assertHead(newProjectName, "refs/heads/master"); - } - - @Test - public void testCreateProject() throws Exception { - final String newProjectName = "newProject"; + public void testCreateProjectHttp() throws Exception { + String newProjectName = name("newProject"); RestResponse r = adminSession.put("/projects/" + newProjectName); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class); @@ -82,11 +69,49 @@ } @Test - public void testCreateProjectWithGitSuffix() throws Exception { - final String newProjectName = "newProject"; - RestResponse r = adminSession.put("/projects/" + newProjectName + ".git"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class); + public void testCreateProjectHttpWhenProjectAlreadyExists_Conflict() + throws Exception { + RestResponse r = adminSession.put("/projects/" + allProjects.get()); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); + } + + @Test + public void testCreateProjectHttpWhenProjectAlreadyExists_PreconditionFailed() + throws Exception { + RestResponse r = adminSession.putWithHeader("/projects/" + allProjects.get(), + new BasicHeader(HttpHeaders.IF_NONE_MATCH, "*")); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_PRECONDITION_FAILED); + } + + @Test + @UseLocalDisk + public void testCreateProjectHttpWithUnreasonableName_BadRequest() + throws Exception { + RestResponse r = adminSession.put("/projects/" + Url.encode(name("invalid/../name"))); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); + } + + @Test + public void testCreateProjectHttpWithNameMismatch_BadRequest() throws Exception { + ProjectInput in = new ProjectInput(); + in.name = name("otherName"); + RestResponse r = adminSession.put("/projects/" + name("someName"), in); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); + } + + @Test + public void testCreateProjectHttpWithInvalidRefName_BadRequest() + throws Exception { + ProjectInput in = new ProjectInput(); + in.branches = Collections.singletonList(name("invalid ref name")); + RestResponse r = adminSession.put("/projects/" + name("newProject"), in); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); + } + + @Test + public void testCreateProject() throws Exception { + String newProjectName = name("newProject"); + ProjectInfo p = gApi.projects().create(newProjectName).get(); assertThat(p.name).isEqualTo(newProjectName); ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName)); assertThat(projectState).isNotNull(); @@ -95,25 +120,28 @@ } @Test - public void testCreateProjectWithNameMismatch_BadRequest() throws Exception { - ProjectInput in = new ProjectInput(); - in.name = "otherName"; - RestResponse r = adminSession.put("/projects/someName", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST); + public void testCreateProjectWithGitSuffix() throws Exception { + String newProjectName = name("newProject"); + ProjectInfo p = gApi.projects().create(newProjectName + ".git").get(); + assertThat(p.name).isEqualTo(newProjectName); + ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName)); + assertThat(projectState).isNotNull(); + assertProjectInfo(projectState.getProject(), p); + assertHead(newProjectName, "refs/heads/master"); } @Test public void testCreateProjectWithProperties() throws Exception { - final String newProjectName = "newProject"; + String newProjectName = name("newProject"); ProjectInput in = new ProjectInput(); + in.name = newProjectName; in.description = "Test description"; in.submitType = SubmitType.CHERRY_PICK; in.useContributorAgreements = InheritableBoolean.TRUE; in.useSignedOffBy = InheritableBoolean.TRUE; in.useContentMerge = InheritableBoolean.TRUE; in.requireChangeId = InheritableBoolean.TRUE; - RestResponse r = adminSession.put("/projects/" + newProjectName, in); - ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class); + ProjectInfo p = gApi.projects().create(in).get(); assertThat(p.name).isEqualTo(newProjectName); Project project = projectCache.get(new Project.NameKey(newProjectName)).getProject(); assertProjectInfo(project, p); @@ -127,13 +155,16 @@ @Test public void testCreateChildProject() throws Exception { - final String parentName = "parent"; - RestResponse r = adminSession.put("/projects/" + parentName); - r.consume(); - final String childName = "child"; + String parentName = name("parent"); ProjectInput in = new ProjectInput(); + in.name = parentName; + gApi.projects().create(in); + + String childName = name("child"); + in = new ProjectInput(); + in.name = childName; in.parent = parentName; - r = adminSession.put("/projects/" + childName, in); + gApi.projects().create(in); Project project = projectCache.get(new Project.NameKey(childName)).getProject(); assertThat(project.getParentName()).isEqualTo(in.parent); } @@ -142,21 +173,22 @@ public void testCreateChildProjectUnderNonExistingParent_UnprocessableEntity() throws Exception { ProjectInput in = new ProjectInput(); + in.name = name("newProjectName"); in.parent = "non-existing-project"; - RestResponse r = adminSession.put("/projects/child", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_UNPROCESSABLE_ENTITY); + assertCreateFails(in, UnprocessableEntityException.class); } @Test public void testCreateProjectWithOwner() throws Exception { - final String newProjectName = "newProject"; + String newProjectName = name("newProject"); ProjectInput in = new ProjectInput(); + in.name = newProjectName; in.owners = Lists.newArrayListWithCapacity(3); in.owners.add("Anonymous Users"); // by name in.owners.add(SystemGroupBackend.REGISTERED_USERS.get()); // by UUID in.owners.add(Integer.toString(groupCache.get( new AccountGroup.NameKey("Administrators")).getId().get())); // by ID - adminSession.put("/projects/" + newProjectName, in); + gApi.projects().create(in); ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName)); Set<AccountGroup.UUID> expectedOwnerIds = Sets.newHashSetWithExpectedSize(3); expectedOwnerIds.add(SystemGroupBackend.ANONYMOUS_USERS); @@ -169,39 +201,42 @@ public void testCreateProjectWithNonExistingOwner_UnprocessableEntity() throws Exception { ProjectInput in = new ProjectInput(); + in.name = name("newProjectName"); in.owners = Collections.singletonList("non-existing-group"); - RestResponse r = adminSession.put("/projects/newProject", in); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_UNPROCESSABLE_ENTITY); + assertCreateFails(in, UnprocessableEntityException.class); } @Test public void testCreatePermissionOnlyProject() throws Exception { - final String newProjectName = "newProject"; + String newProjectName = name("newProject"); ProjectInput in = new ProjectInput(); + in.name = newProjectName; in.permissionsOnly = true; - adminSession.put("/projects/" + newProjectName, in); + gApi.projects().create(in); assertHead(newProjectName, RefNames.REFS_CONFIG); } @Test public void testCreateProjectWithEmptyCommit() throws Exception { - final String newProjectName = "newProject"; + String newProjectName = name("newProject"); ProjectInput in = new ProjectInput(); + in.name = newProjectName; in.createEmptyCommit = true; - adminSession.put("/projects/" + newProjectName, in); + gApi.projects().create(in); assertEmptyCommit(newProjectName, "refs/heads/master"); } @Test public void testCreateProjectWithBranches() throws Exception { - final String newProjectName = "newProject"; + String newProjectName = name("newProject"); ProjectInput in = new ProjectInput(); + in.name = newProjectName; in.createEmptyCommit = true; in.branches = Lists.newArrayListWithCapacity(3); in.branches.add("refs/heads/test"); in.branches.add("refs/heads/master"); in.branches.add("release"); // without 'refs/heads' prefix - adminSession.put("/projects/" + newProjectName, in); + gApi.projects().create(in); assertHead(newProjectName, "refs/heads/test"); assertEmptyCommit(newProjectName, "refs/heads/test", "refs/heads/master", "refs/heads/release"); @@ -209,15 +244,18 @@ @Test public void testCreateProjectWithoutCapability_Forbidden() throws Exception { - RestResponse r = userSession.put("/projects/newProject"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); + setApiUser(user); + ProjectInput in = new ProjectInput(); + in.name = name("newProject"); + assertCreateFails(in, AuthException.class); } @Test public void testCreateProjectWhenProjectAlreadyExists_Conflict() throws Exception { - RestResponse r = adminSession.put("/projects/All-Projects"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); + ProjectInput in = new ProjectInput(); + in.name = allProjects.get(); + assertCreateFails(in, ResourceConflictException.class); } private AccountGroup.UUID groupUuid(String groupName) { @@ -251,4 +289,14 @@ } } } + + private void assertCreateFails(ProjectInput in, + Class<? extends RestApiException> errType) throws Exception { + try { + gApi.projects().create(in); + fail("Expected " + errType.getSimpleName()); + } catch (RestApiException expected) { + assertThat(expected).isInstanceOf(errType); + } + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java index 8be6c92..a026d76 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
@@ -14,21 +14,25 @@ package com.google.gerrit.acceptance.rest.project; -import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.server.project.Util.block; +import static org.junit.Assert.fail; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.common.data.Permission; +import com.google.gerrit.extensions.api.projects.BranchApi; +import com.google.gerrit.extensions.api.projects.BranchInput; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.server.git.ProjectConfig; -import org.apache.http.HttpStatus; import org.junit.Before; import org.junit.Test; +@NoHttpd public class DeleteBranchIT extends AbstractDaemonTest { private Branch.NameKey branch; @@ -36,62 +40,31 @@ @Before public void setUp() throws Exception { branch = new Branch.NameKey(project, "test"); - adminSession.put("/projects/" + project.get() - + "/branches/" + branch.getShortName()).consume(); + branch().create(new BranchInput()); } @Test public void deleteBranch_Forbidden() throws Exception { - RestResponse r = - userSession.delete("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); - r.consume(); + setApiUser(user); + assertDeleteForbidden(); } @Test public void deleteBranchByAdmin() throws Exception { - RestResponse r = - adminSession.delete("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - r.consume(); - - r = adminSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); - r.consume(); + assertDeleteSucceeds(); } @Test public void deleteBranchByProjectOwner() throws Exception { grantOwner(); - - RestResponse r = - userSession.delete("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - r.consume(); - - r = userSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); - r.consume(); + setApiUser(user); + assertDeleteSucceeds(); } @Test public void deleteBranchByAdminForcePushBlocked() throws Exception { blockForcePush(); - RestResponse r = - adminSession.delete("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - r.consume(); - - r = adminSession.get("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); - r.consume(); + assertDeleteSucceeds(); } @Test @@ -99,20 +72,43 @@ throws Exception { grantOwner(); blockForcePush(); - RestResponse r = - userSession.delete("/projects/" + project.get() - + "/branches/" + branch.getShortName()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); - r.consume(); + setApiUser(user); + assertDeleteForbidden(); } private void blockForcePush() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); block(cfg, Permission.PUSH, ANONYMOUS_USERS, "refs/heads/*").setForce(true); - saveProjectConfig(allProjects, cfg); + saveProjectConfig(project, cfg); } private void grantOwner() throws Exception { allow(Permission.OWNER, REGISTERED_USERS, "refs/*"); } + + private BranchApi branch() throws Exception { + return gApi.projects() + .name(branch.getParentKey().get()) + .branch(branch.get()); + } + + private void assertDeleteSucceeds() throws Exception { + branch().delete(); + try { + branch().get(); + fail("Expected ResourceNotFoundException"); + } catch (ResourceNotFoundException expected) { + // Expected. + } + } + + private void assertDeleteForbidden() throws Exception { + try { + branch().delete(); + fail("Expected AuthException"); + } catch (AuthException expected) { + // Expected. + } + branch().get(); + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java index 6aa3af6..d680541 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
@@ -15,7 +15,6 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GcAssert; @@ -39,8 +38,7 @@ @Before public void setUp() throws Exception { - project2 = new Project.NameKey("p2"); - createProject(sshSession, project2.get()); + project2 = createProject("p2"); } @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java index f49408e..ea3a5db 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
@@ -15,87 +15,66 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectInfo; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.acceptance.SshSession; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.reviewdb.client.Project; -import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; - +@NoHttpd public class GetChildProjectIT extends AbstractDaemonTest { @Test public void getNonExistingChildProject_NotFound() throws Exception { - assertThat( - GET("/projects/" + allProjects.get() + "/children/non-existing") - .getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); + assertChildNotFound(allProjects, "non-existing"); } @Test public void getNonChildProject_NotFound() throws Exception { - SshSession sshSession = new SshSession(server, admin); - Project.NameKey p1 = new Project.NameKey("p1"); - createProject(sshSession, p1.get()); - Project.NameKey p2 = new Project.NameKey("p2"); - createProject(sshSession, p2.get()); - sshSession.close(); - assertThat( - GET("/projects/" + p1.get() + "/children/" + p2.get()).getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); + Project.NameKey p1 = createProject("p1"); + Project.NameKey p2 = createProject("p2"); + + assertChildNotFound(p1, p2.get()); } @Test public void getChildProject() throws Exception { - SshSession sshSession = new SshSession(server, admin); - Project.NameKey child = new Project.NameKey("p1"); - createProject(sshSession, child.get()); - sshSession.close(); - RestResponse r = - GET("/projects/" + allProjects.get() + "/children/" + child.get()); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - ProjectInfo childInfo = - newGson().fromJson(r.getReader(), ProjectInfo.class); + Project.NameKey child = createProject("p1"); + ProjectInfo childInfo = gApi.projects().name(allProjects.get()) + .child(child.get()).get(); + assertProjectInfo(projectCache.get(child).getProject(), childInfo); } @Test public void getGrandChildProject_NotFound() throws Exception { - SshSession sshSession = new SshSession(server, admin); - Project.NameKey child = new Project.NameKey("p1"); - createProject(sshSession, child.get()); - Project.NameKey grandChild = new Project.NameKey("p1.1"); - createProject(sshSession, grandChild.get(), child); - sshSession.close(); - assertThat( - GET("/projects/" + allProjects.get() + "/children/" + grandChild.get()) - .getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); + Project.NameKey child = createProject("p1"); + Project.NameKey grandChild = createProject("p1.1", child); + + assertChildNotFound(allProjects, grandChild.get()); } @Test public void getGrandChildProjectWithRecursiveFlag() throws Exception { - SshSession sshSession = new SshSession(server, admin); - Project.NameKey child = new Project.NameKey("p1"); - createProject(sshSession, child.get()); - Project.NameKey grandChild = new Project.NameKey("p1.1"); - createProject(sshSession, grandChild.get(), child); - sshSession.close(); - RestResponse r = - GET("/projects/" + allProjects.get() + "/children/" + grandChild.get() - + "?recursive"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - ProjectInfo grandChildInfo = - newGson().fromJson(r.getReader(), ProjectInfo.class); - assertProjectInfo(projectCache.get(grandChild).getProject(), grandChildInfo); + Project.NameKey child = createProject("p1"); + Project.NameKey grandChild = createProject("p1.1", child); + + ProjectInfo grandChildInfo = gApi.projects().name(allProjects.get()) + .child(grandChild.get()).get(true); + assertProjectInfo( + projectCache.get(grandChild).getProject(), grandChildInfo); } - private RestResponse GET(String endpoint) throws IOException { - return adminSession.get(endpoint); + private void assertChildNotFound(Project.NameKey parent, String child) + throws Exception { + try { + gApi.projects().name(parent.get()).child(child); + } catch (ResourceNotFoundException e) { + assertThat(e.getMessage()).contains(child); + } } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetCommitIT.java index 4a20957..d3677ab 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetCommitIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
@@ -15,13 +15,12 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.Permission; import com.google.gerrit.extensions.common.CommitInfo; import com.google.gerrit.server.git.ProjectConfig; @@ -40,13 +39,8 @@ @Before public void setUp() throws Exception { - repo = new TestRepository<>(repoManager.openRepository(project)); - - ProjectConfig pc = projectCache.checkedGet(allProjects).getConfig(); - for (AccessSection sec : pc.getAccessSections()) { - sec.removePermission(Permission.READ); - } - saveProjectConfig(allProjects, pc); + repo = GitUtil.newTestRepository(repoManager.openRepository(project)); + blockRead(project, "refs/*"); } @After @@ -64,7 +58,7 @@ @Test public void getMergedCommit_Found() throws Exception { - allow(Permission.READ, REGISTERED_USERS, "refs/heads/*"); + unblockRead(); RevCommit commit = repo.parseBody(repo.branch("master") .commit() .message("Create\n\nNew commit\n") @@ -98,9 +92,9 @@ @Test public void getOpenChange_Found() throws Exception { - allow(Permission.READ, REGISTERED_USERS, "refs/heads/*"); - PushOneCommit.Result r = pushFactory.create(db, admin.getIdent()) - .to(git, "refs/for/master"); + unblockRead(); + PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), testRepo) + .to("refs/for/master"); r.assertOkStatus(); CommitInfo info = getCommit(r.getCommitId()); @@ -123,12 +117,18 @@ @Test public void getOpenChange_NotFound() throws Exception { - PushOneCommit.Result r = pushFactory.create(db, admin.getIdent()) - .to(git, "refs/for/master"); + PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), testRepo) + .to("refs/for/master"); r.assertOkStatus(); assertNotFound(r.getCommitId()); } + private void unblockRead() throws Exception { + ProjectConfig pc = projectCache.checkedGet(project).getConfig(); + pc.getAccessSection("refs/*").remove(new Permission(Permission.READ)); + saveProjectConfig(project, pc); + } + private void assertNotFound(ObjectId id) throws Exception { RestResponse r = userSession.get( "/projects/" + project.get() + "/commits/" + id.name());
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java index 761e282..24b1770 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
@@ -17,35 +17,31 @@ import static com.google.common.truth.Truth.assertThat; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import org.apache.http.HttpStatus; import org.junit.Test; +@NoHttpd public class GetProjectIT extends AbstractDaemonTest { @Test public void getProject() throws Exception { String name = project.get(); - RestResponse r = adminSession.get("/projects/" + name); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class); + ProjectInfo p = gApi.projects().name(name).get(); assertThat(p.name).isEqualTo(name); } @Test public void getProjectWithGitSuffix() throws Exception { String name = project.get(); - RestResponse r = adminSession.get("/projects/" + name + ".git"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class); + ProjectInfo p = gApi.projects().name(name).get(); assertThat(p.name).isEqualTo(name); } - @Test + @Test(expected = ResourceNotFoundException.class) public void getProjectNotExisting() throws Exception { - RestResponse r = adminSession.get("/projects/does-not-exist"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); + gApi.projects().name("does-not-exist").get(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java index 48f9ad89..3793c82 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -14,102 +14,87 @@ package com.google.gerrit.acceptance.rest.project; -import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; import static com.google.gerrit.acceptance.rest.project.BranchAssert.assertBranches; +import static com.google.gerrit.acceptance.rest.project.BranchAssert.assertRefNames; +import static org.junit.Assert.fail; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.server.project.ListBranches.BranchInfo; -import com.google.gson.reflect.TypeToken; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.extensions.api.projects.BranchInfo; +import com.google.gerrit.extensions.api.projects.ProjectApi.ListBranchesRequest; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; -import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - +@NoHttpd public class ListBranchesIT extends AbstractDaemonTest { @Test public void listBranchesOfNonExistingProject_NotFound() throws Exception { - assertThat(GET("/projects/non-existing/branches").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); + try { + gApi.projects().name("non-existing").branches().get(); + fail("Expected ResourceNotFoundException"); + } catch (ResourceNotFoundException expected) { + // Expected. + } } @Test public void listBranchesOfNonVisibleProject_NotFound() throws Exception { blockRead(project, "refs/*"); - assertThat( - userSession.get("/projects/" + project.get() + "/branches") - .getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); + setApiUser(user); + try { + gApi.projects().name(project.get()).branches().get(); + fail("Expected ResourceNotFoundException"); + } catch (ResourceNotFoundException expected) { + // Expected. + } } @Test + @TestProjectInput(createEmptyCommit = false) public void listBranchesOfEmptyProject() throws Exception { - Project.NameKey emptyProject = new Project.NameKey("empty"); - createProject(sshSession, emptyProject.get(), null, false); - RestResponse r = adminSession.get("/projects/" + emptyProject.get() + "/branches"); - List<BranchInfo> expected = Lists.asList( - new BranchInfo("refs/meta/config", null, false), - new BranchInfo[] { - new BranchInfo("HEAD", null, false) - }); - assertBranches(expected, toBranchInfoList(r)); + assertBranches(ImmutableList.of( + branch("HEAD", null, false), + branch("refs/meta/config", null, false)), + list().get()); } @Test public void listBranches() throws Exception { - pushTo("refs/heads/master"); - String masterCommit = git.getRepository().getRef("master").getTarget().getObjectId().getName(); - pushTo("refs/heads/dev"); - String devCommit = git.getRepository().getRef("master").getTarget().getObjectId().getName(); - RestResponse r = adminSession.get("/projects/" + project.get() + "/branches"); - List<BranchInfo> expected = Lists.asList( - new BranchInfo("refs/meta/config", null, false), - new BranchInfo[] { - new BranchInfo("HEAD", "master", false), - new BranchInfo("refs/heads/master", masterCommit, false), - new BranchInfo("refs/heads/dev", devCommit, true) - }); - List<BranchInfo> result = toBranchInfoList(r); - assertBranches(expected, result); - - // verify correct sorting - assertThat(result.get(0).ref).isEqualTo("HEAD"); - assertThat(result.get(1).ref).isEqualTo("refs/meta/config"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/dev"); - assertThat(result.get(3).ref).isEqualTo("refs/heads/master"); + String master = pushTo("refs/heads/master").getCommit().name(); + String dev = pushTo("refs/heads/dev").getCommit().name(); + assertBranches(ImmutableList.of( + branch("HEAD", "master", false), + branch("refs/meta/config", null, false), + branch("refs/heads/dev", dev, true), + branch("refs/heads/master", master, false)), + list().get()); } @Test public void listBranchesSomeHidden() throws Exception { blockRead(project, "refs/heads/dev"); - pushTo("refs/heads/master"); - String masterCommit = git.getRepository().getRef("master").getTarget().getObjectId().getName(); + String master = pushTo("refs/heads/master").getCommit().name(); pushTo("refs/heads/dev"); - RestResponse r = userSession.get("/projects/" + project.get() + "/branches"); + setApiUser(user); // refs/meta/config is hidden since user is no project owner - List<BranchInfo> expected = Lists.asList( - new BranchInfo("HEAD", "master", false), - new BranchInfo[] { - new BranchInfo("refs/heads/master", masterCommit, false), - }); - assertBranches(expected, toBranchInfoList(r)); + assertBranches(ImmutableList.of( + branch("HEAD", "master", false), + branch("refs/heads/master", master, false)), + list().get()); } @Test public void listBranchesHeadHidden() throws Exception { blockRead(project, "refs/heads/master"); pushTo("refs/heads/master"); - pushTo("refs/heads/dev"); - String devCommit = git.getRepository().getRef("master").getTarget().getObjectId().getName(); - RestResponse r = userSession.get("/projects/" + project.get() + "/branches"); + String dev = pushTo("refs/heads/dev").getCommit().name(); + setApiUser(user); // refs/meta/config is hidden since user is no project owner - assertBranches(Collections.singletonList(new BranchInfo("refs/heads/dev", - devCommit, false)), toBranchInfoList(r)); + assertBranches(ImmutableList.of(branch("refs/heads/dev", dev, false)), + list().get()); } @Test @@ -119,47 +104,40 @@ pushTo("refs/heads/someBranch2"); pushTo("refs/heads/someBranch3"); - // using only limit - RestResponse r = - adminSession.get("/projects/" + project.get() + "/branches?n=4"); - List<BranchInfo> result = toBranchInfoList(r); - assertThat(result).hasSize(4); - assertThat(result.get(0).ref).isEqualTo("HEAD"); - assertThat(result.get(1).ref).isEqualTo("refs/meta/config"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/master"); - assertThat(result.get(3).ref).isEqualTo("refs/heads/someBranch1"); + // Using only limit. + assertRefNames(ImmutableList.of( + "HEAD", + "refs/meta/config", + "refs/heads/master", + "refs/heads/someBranch1"), + list().withLimit(4).get()); - // limit higher than total number of branches - r = adminSession.get("/projects/" + project.get() + "/branches?n=25"); - result = toBranchInfoList(r); - assertThat(result).hasSize(6); - assertThat(result.get(0).ref).isEqualTo("HEAD"); - assertThat(result.get(1).ref).isEqualTo("refs/meta/config"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/master"); - assertThat(result.get(3).ref).isEqualTo("refs/heads/someBranch1"); - assertThat(result.get(4).ref).isEqualTo("refs/heads/someBranch2"); - assertThat(result.get(5).ref).isEqualTo("refs/heads/someBranch3"); + // Limit higher than total number of branches. + assertRefNames(ImmutableList.of( + "HEAD", + "refs/meta/config", + "refs/heads/master", + "refs/heads/someBranch1", + "refs/heads/someBranch2", + "refs/heads/someBranch3"), + list().withLimit(25).get()); - // using skip only - r = adminSession.get("/projects/" + project.get() + "/branches?s=2"); - result = toBranchInfoList(r); - assertThat(result).hasSize(4); - assertThat(result.get(0).ref).isEqualTo("refs/heads/master"); - assertThat(result.get(1).ref).isEqualTo("refs/heads/someBranch1"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/someBranch2"); - assertThat(result.get(3).ref).isEqualTo("refs/heads/someBranch3"); + // Using start only. + assertRefNames(ImmutableList.of( + "refs/heads/master", + "refs/heads/someBranch1", + "refs/heads/someBranch2", + "refs/heads/someBranch3"), + list().withStart(2).get()); - // skip more branches than the number of available branches - r = adminSession.get("/projects/" + project.get() + "/branches?s=7"); - result = toBranchInfoList(r); - assertThat(result).isEmpty(); + // Skip more branches than the number of available branches. + assertRefNames(ImmutableList.<String> of(), list().withStart(7).get()); - // using skip and limit - r = adminSession.get("/projects/" + project.get() + "/branches?s=2&n=2"); - result = toBranchInfoList(r); - assertThat(result).hasSize(2); - assertThat(result.get(0).ref).isEqualTo("refs/heads/master"); - assertThat(result.get(1).ref).isEqualTo("refs/heads/someBranch1"); + // Ssing start and limit. + assertRefNames(ImmutableList.of( + "refs/heads/master", + "refs/heads/someBranch1"), + list().withStart(2).withLimit(2).get()); } @Test @@ -169,38 +147,34 @@ pushTo("refs/heads/someBranch2"); pushTo("refs/heads/someBranch3"); - //using substring - RestResponse r = - adminSession.get("/projects/" + project.get() + "/branches?m=some"); - List<BranchInfo> result = toBranchInfoList(r); - assertThat(result).hasSize(3); - assertThat(result.get(0).ref).isEqualTo("refs/heads/someBranch1"); - assertThat(result.get(1).ref).isEqualTo("refs/heads/someBranch2"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/someBranch3"); + // Using substring. + assertRefNames(ImmutableList.of( + "refs/heads/someBranch1", + "refs/heads/someBranch2", + "refs/heads/someBranch3"), + list().withSubstring("some").get()); - r = adminSession.get("/projects/" + project.get() + "/branches?m=Branch"); - result = toBranchInfoList(r); - assertThat(result).hasSize(3); - assertThat(result.get(0).ref).isEqualTo("refs/heads/someBranch1"); - assertThat(result.get(1).ref).isEqualTo("refs/heads/someBranch2"); - assertThat(result.get(2).ref).isEqualTo("refs/heads/someBranch3"); + assertRefNames(ImmutableList.of( + "refs/heads/someBranch1", + "refs/heads/someBranch2", + "refs/heads/someBranch3"), + list().withSubstring("Branch").get()); - //using regex - r = adminSession.get("/projects/" + project.get() + "/branches?r=.*ast.*r"); - result = toBranchInfoList(r); - assertThat(result).hasSize(1); - assertThat(result.get(0).ref).isEqualTo("refs/heads/master"); + // Using regex. + assertRefNames(ImmutableList.of("refs/heads/master"), + list().withRegex(".*ast.*r").get()); } - private RestResponse GET(String endpoint) throws IOException { - return adminSession.get(endpoint); + private ListBranchesRequest list() throws Exception { + return gApi.projects().name(project.get()).branches(); } - private static List<BranchInfo> toBranchInfoList(RestResponse r) - throws IOException { - List<BranchInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<List<BranchInfo>>() {}.getType()); - return result; + private static BranchInfo branch(String ref, String revision, + boolean canDelete) { + BranchInfo info = new BranchInfo(); + info.ref = ref; + info.revision = revision; + info.canDelete = canDelete ? true : null; + return info; } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java index 0d3b467..80ad493 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
@@ -15,84 +15,54 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; -import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjects; +import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertThatNameList; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.reviewdb.client.Project; -import com.google.gson.reflect.TypeToken; -import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - +@NoHttpd public class ListChildProjectsIT extends AbstractDaemonTest { @Test public void listChildrenOfNonExistingProject_NotFound() throws Exception { - assertThat(GET("/projects/non-existing/children/").getStatusCode()) - .isEqualTo(HttpStatus.SC_NOT_FOUND); + try { + gApi.projects().name(name("non-existing")).child("children"); + } catch (ResourceNotFoundException e) { + assertThat(e.getMessage()).contains("non-existing"); + } } @Test public void listNoChildren() throws Exception { - RestResponse r = GET("/projects/" + allProjects.get() + "/children/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - List<ProjectInfo> projectInfoList = toProjectInfoList(r); - // Project 'p' was already created in the base class - assertThat(projectInfoList).hasSize(2); + assertThatNameList(gApi.projects().name(project.get()).children()) + .isEmpty(); } @Test public void listChildren() throws Exception { - Project.NameKey existingProject = new Project.NameKey("p"); - Project.NameKey child1 = new Project.NameKey("p1"); - createProject(sshSession, child1.get()); - Project.NameKey child2 = new Project.NameKey("p2"); - createProject(sshSession, child2.get()); - createProject(sshSession, "p1.1", child1); + Project.NameKey child1 = createProject("p1"); + Project.NameKey child1_1 = createProject("p1.1", child1); + Project.NameKey child1_2 = createProject("p1.2", child1); - RestResponse r = GET("/projects/" + allProjects.get() + "/children/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertProjects( - Arrays.asList( - new Project.NameKey("All-Users"), - existingProject, child1, child2), - toProjectInfoList(r)); + assertThatNameList(gApi.projects().name(child1.get()).children()) + .containsExactly(child1_1, child1_2).inOrder(); } @Test public void listChildrenRecursively() throws Exception { - Project.NameKey child1 = new Project.NameKey("p1"); - createProject(sshSession, child1.get()); - createProject(sshSession, "p2"); - Project.NameKey child1_1 = new Project.NameKey("p1.1"); - createProject(sshSession, child1_1.get(), child1); - Project.NameKey child1_2 = new Project.NameKey("p1.2"); - createProject(sshSession, child1_2.get(), child1); - Project.NameKey child1_1_1 = new Project.NameKey("p1.1.1"); - createProject(sshSession, child1_1_1.get(), child1_1); - Project.NameKey child1_1_1_1 = new Project.NameKey("p1.1.1.1"); - createProject(sshSession, child1_1_1_1.get(), child1_1_1); + Project.NameKey child1 = createProject("p1"); + createProject("p2"); + Project.NameKey child1_1 = createProject("p1.1", child1); + Project.NameKey child1_2 = createProject("p1.2", child1); + Project.NameKey child1_1_1 = createProject("p1.1.1", child1_1); + Project.NameKey child1_1_1_1 = createProject("p1.1.1.1", child1_1_1); - RestResponse r = GET("/projects/" + child1.get() + "/children/?recursive"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertProjects(Arrays.asList(child1_1, child1_2, - child1_1_1, child1_1_1_1), toProjectInfoList(r)); - } - - private static List<ProjectInfo> toProjectInfoList(RestResponse r) - throws IOException { - return newGson().fromJson(r.getReader(), - new TypeToken<List<ProjectInfo>>() {}.getType()); - } - - private RestResponse GET(String endpoint) throws IOException { - return adminSession.get(endpoint); + assertThatNameList(gApi.projects().name(child1.get()).children(true)) + .containsExactly(child1_1, child1_1_1, child1_1_1_1, child1_2) + .inOrder(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java index 0f5bed1..1e51571 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -15,25 +15,31 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; -import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjects; +import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertThatNameList; +import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.RestResponse; -import com.google.gerrit.extensions.api.projects.ProjectInput; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.extensions.api.projects.Projects.ListRequest; +import com.google.gerrit.extensions.api.projects.Projects.ListRequest.FilterType; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.AllUsersName; -import com.google.gson.reflect.TypeToken; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.project.Util; import com.google.inject.Inject; -import org.apache.http.HttpStatus; import org.junit.Test; -import java.io.IOException; -import java.util.Arrays; +import java.util.List; import java.util.Map; +@NoHttpd public class ListProjectsIT extends AbstractDaemonTest { @Inject @@ -41,206 +47,174 @@ @Test public void listProjects() throws Exception { - Project.NameKey someProject = new Project.NameKey("some-project"); - createProject(sshSession, someProject.get()); + Project.NameKey someProject = createProject("some-project"); + assertThatNameList(filter(gApi.projects().list().get())) + .containsExactly(allProjects, allUsers, project, someProject).inOrder(); + } - RestResponse r = GET("/projects/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertProjects(Arrays.asList(allUsers, someProject, project), - result.values()); + @Test + public void listProjectsFiltersInvisibleProjects() throws Exception { + setApiUser(user); + assertThatNameList(gApi.projects().list().get()).contains(project); + + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + Util.block(cfg, Permission.READ, REGISTERED_USERS, "refs/*"); + saveProjectConfig(project, cfg); + + assertThatNameList(filter(gApi.projects().list().get())) + .doesNotContain(project); } @Test public void listProjectsWithBranch() throws Exception { - RestResponse r = GET("/projects/?b=master"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertThat(result.get(project.get())).isNotNull(); - assertThat(result.get(project.get()).branches).isNotNull(); - assertThat(result.get(project.get()).branches).hasSize(1); - assertThat(result.get(project.get()).branches.get("master")).isNotNull(); + Map<String, ProjectInfo> result = gApi.projects().list() + .addShowBranch("master").getAsMap(); + assertThat(result).containsKey(project.get()); + ProjectInfo info = result.get(project.get()); + assertThat(info.branches).isNotNull(); + assertThat(info.branches).hasSize(1); + assertThat(info.branches.get("master")).isNotNull(); } @Test + @TestProjectInput(description = "Description of some-project") public void listProjectWithDescription() throws Exception { - ProjectInput projectInput = new ProjectInput(); - projectInput.name = "some-project"; - projectInput.description = "Description of some-project"; - gApi.projects().name(projectInput.name).create(projectInput); - // description not be included in the results by default. - RestResponse r = GET("/projects/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertThat(result.get(projectInput.name)).isNotNull(); - assertThat(result.get(projectInput.name).description).isNull(); + Map<String, ProjectInfo> result = gApi.projects().list().getAsMap(); + assertThat(result).containsKey(project.get()); + assertThat(result.get(project.get()).description).isNull(); - r = GET("/projects/?d"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertThat(result.get(projectInput.name)).isNotNull(); - assertThat(result.get(projectInput.name).description).isEqualTo( - projectInput.description); + result = gApi.projects().list().withDescription(true).getAsMap(); + assertThat(result).containsKey(project.get()); + assertThat(result.get(project.get()).description).isEqualTo( + "Description of some-project"); } @Test public void listProjectsWithLimit() throws Exception { for (int i = 0; i < 5; i++) { - createProject(sshSession, new Project.NameKey("someProject" + i).get()); + createProject("someProject" + i); } - RestResponse r = GET("/projects/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertThat(result).hasSize(7); // 5 plus 2 existing projects: p and - // All-Users - - r = GET("/projects/?n=2"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertThat(result).hasSize(2); + String p = name(""); + // 5, plus p which was automatically created. + int n = 6; + for (int i = 1; i <= n + 2; i++) { + assertThatNameList(gApi.projects().list().withPrefix(p) + .withLimit(i).get()) + .hasSize(Math.min(i, n)); + } } @Test public void listProjectsWithPrefix() throws Exception { - Project.NameKey someProject = new Project.NameKey("some-project"); - createProject(sshSession, someProject.get()); - Project.NameKey someOtherProject = - new Project.NameKey("some-other-project"); - createProject(sshSession, someOtherProject.get()); - Project.NameKey projectAwesome = new Project.NameKey("project-awesome"); - createProject(sshSession, projectAwesome.get()); + Project.NameKey someProject = createProject("some-project"); + Project.NameKey someOtherProject = createProject("some-other-project"); + createProject("project-awesome"); - assertThat(GET("/projects/?p=some&r=.*").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - assertThat(GET("/projects/?p=some&m=some").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - - RestResponse r = GET("/projects/?p=some"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertProjects(Arrays.asList(someProject, someOtherProject), - result.values()); + String p = name("some"); + assertBadRequest(gApi.projects().list().withPrefix(p).withRegex(".*")); + assertBadRequest(gApi.projects().list().withPrefix(p).withSubstring(p)); + assertThatNameList(filter(gApi.projects().list().withPrefix(p).get())) + .containsExactly(someOtherProject, someProject).inOrder(); } @Test public void listProjectsWithRegex() throws Exception { - Project.NameKey someProject = new Project.NameKey("some-project"); - createProject(sshSession, someProject.get()); - Project.NameKey someOtherProject = - new Project.NameKey("some-other-project"); - createProject(sshSession, someOtherProject.get()); - Project.NameKey projectAwesome = new Project.NameKey("project-awesome"); - createProject(sshSession, projectAwesome.get()); + Project.NameKey someProject = createProject("some-project"); + Project.NameKey someOtherProject = createProject("some-other-project"); + Project.NameKey projectAwesome = createProject("project-awesome"); - assertThat(GET("/projects/?r=[.*some").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - assertThat(GET("/projects/?r=.*&p=s").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - assertThat(GET("/projects/?r=.*&m=s").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); + assertBadRequest(gApi.projects().list().withRegex("[.*")); + assertBadRequest(gApi.projects().list().withRegex(".*").withPrefix("p")); + assertBadRequest(gApi.projects().list().withRegex(".*").withSubstring("p")); - RestResponse r = GET("/projects/?r=.*some"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertProjects(Arrays.asList(projectAwesome), result.values()); - - r = GET("/projects/?r=some-project$"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertProjects(Arrays.asList(someProject), result.values()); - - r = GET("/projects/?r=.*"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertProjects(Arrays.asList(someProject, someOtherProject, projectAwesome, - project, allUsers), result.values()); + assertThatNameList(filter(gApi.projects().list().withRegex(".*some").get())) + .containsExactly(projectAwesome); + String r = name("some-project$").replace(".", "\\."); + assertThatNameList(filter(gApi.projects().list().withRegex(r).get())) + .containsExactly(someProject); + assertThatNameList(filter(gApi.projects().list().withRegex(".*").get())) + .containsExactly(allProjects, allUsers, project, projectAwesome, + someOtherProject, someProject) + .inOrder(); } @Test - public void listProjectsWithSkip() throws Exception { + public void listProjectsWithStart() throws Exception { for (int i = 0; i < 5; i++) { - createProject(sshSession, new Project.NameKey("someProject" + i).get()); + createProject(new Project.NameKey("someProject" + i).get()); } - RestResponse r = GET("/projects/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertThat(result).hasSize(7); // 5 plus 2 existing projects: p and - // All-Users - - r = GET("/projects/?S=6"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertThat(result).hasSize(1); + String p = name(""); + List<ProjectInfo> all = gApi.projects().list().withPrefix(p).get(); + // 5, plus p which was automatically created. + int n = 6; + assertThat(all).hasSize(n); + assertThatNameList(gApi.projects().list().withPrefix(p) + .withStart(n - 1).get()) + .containsExactly(new Project.NameKey(Iterables.getLast(all).name)); } @Test public void listProjectsWithSubstring() throws Exception { - Project.NameKey someProject = new Project.NameKey("some-project"); - createProject(sshSession, someProject.get()); - Project.NameKey someOtherProject = - new Project.NameKey("some-other-project"); - createProject(sshSession, someOtherProject.get()); - Project.NameKey projectAwesome = new Project.NameKey("project-awesome"); - createProject(sshSession, projectAwesome.get()); + Project.NameKey someProject = createProject("some-project"); + Project.NameKey someOtherProject = createProject("some-other-project"); + Project.NameKey projectAwesome = createProject("project-awesome"); - assertThat(GET("/projects/?m=some&r=.*").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - assertThat(GET("/projects/?m=some&p=some").getStatusCode()).isEqualTo( - HttpStatus.SC_BAD_REQUEST); - - RestResponse r = GET("/projects/?m=some"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertProjects( - Arrays.asList(someProject, someOtherProject, projectAwesome), - result.values()); + assertBadRequest(gApi.projects().list().withSubstring("some") + .withRegex(".*")); + assertBadRequest(gApi.projects().list().withSubstring("some") + .withPrefix("some")); + assertThatNameList(filter(gApi.projects().list().withSubstring("some") + .get())) + .containsExactly(projectAwesome, someOtherProject, someProject) + .inOrder(); } @Test public void listProjectsWithTree() throws Exception { - Project.NameKey someParentProject = - new Project.NameKey("some-parent-project"); - createProject(sshSession, someParentProject.get()); + Project.NameKey someParentProject = createProject("some-parent-project"); Project.NameKey someChildProject = - new Project.NameKey("some-child-project"); - createProject(sshSession, someChildProject.get(), someParentProject); + createProject("some-child-project", someParentProject); - RestResponse r = GET("/projects/?tree"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); - assertThat(result.get(someChildProject.get())).isNotNull(); - assertThat(result.get(someChildProject.get()).parent).isEqualTo( - someParentProject.get()); + Map<String, ProjectInfo> result = gApi.projects().list().withTree(true) + .getAsMap(); + assertThat(result).containsKey(someChildProject.get()); + assertThat(result.get(someChildProject.get()).parent) + .isEqualTo(someParentProject.get()); } @Test public void listProjectWithType() throws Exception { - RestResponse r = GET("/projects/?type=PERMISSIONS"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Map<String, ProjectInfo> result = toProjectInfoMap(r); + Map<String, ProjectInfo> result = gApi.projects().list() + .withType(FilterType.PERMISSIONS).getAsMap(); assertThat(result).hasSize(1); - assertThat(result.get(allProjects.get())).isNotNull(); + assertThat(result).containsKey(allProjects.get()); - r = GET("/projects/?type=ALL"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - result = toProjectInfoMap(r); - assertThat(result).hasSize(3); - assertProjects(Arrays.asList(allProjects, allUsers, project), - result.values()); + assertThatNameList(filter(gApi.projects().list().withType(FilterType.ALL) + .get())) + .containsExactly(allProjects, allUsers, project).inOrder(); } - private static Map<String, ProjectInfo> toProjectInfoMap(RestResponse r) - throws IOException { - Map<String, ProjectInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<Map<String, ProjectInfo>>() {}.getType()); - return result; + private static void assertBadRequest(ListRequest req) throws Exception { + try { + req.get(); + } catch (BadRequestException expected) { + // Expected. + } } - private RestResponse GET(String endpoint) throws IOException { - return adminSession.get(endpoint); + private Iterable<ProjectInfo> filter(Iterable<ProjectInfo> infos) { + final String prefix = name(""); + return Iterables.filter(infos, new Predicate<ProjectInfo>() { + @Override + public boolean apply(ProjectInfo in) { + return in.name != null && ( + in.name.equals(allProjects.get()) + || in.name.equals(allUsers.get()) + || in.name.startsWith(prefix)); + } + }); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectAssert.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectAssert.java index 95f46e8..db6df95 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectAssert.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
@@ -15,35 +15,43 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; -import com.google.common.base.Predicate; +import com.google.common.base.Function; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.truth.IterableSubject; import com.google.gerrit.extensions.common.ProjectInfo; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.project.ProjectState; -import java.util.Collection; +import java.util.List; import java.util.Set; public class ProjectAssert { - - public static void assertProjects(Iterable<Project.NameKey> expected, - Collection<ProjectInfo> actual) { - for (final Project.NameKey p : expected) { - ProjectInfo info = Iterables.find(actual, new Predicate<ProjectInfo>() { - @Override - public boolean apply(ProjectInfo info) { - // 'name' is not set if returned in a map, use the id instead. - return new Project.NameKey(info.name != null ? info.name : Url - .decode(info.id)).equals(p); - }}, null); - assertThat(info).isNotNull(); - actual.remove(info); + public static IterableSubject< + ? extends IterableSubject< + ?, Project.NameKey, Iterable<Project.NameKey>>, + Project.NameKey, + Iterable<Project.NameKey>> + assertThatNameList(Iterable<ProjectInfo> actualIt) { + List<ProjectInfo> actual = ImmutableList.copyOf(actualIt); + for (ProjectInfo info : actual) { + assertWithMessage("missing project name").that(info.name).isNotNull(); + assertWithMessage("project name does not match id") + .that(Url.decode(info.id)) + .isEqualTo(info.name); } - assertThat((Iterable<?>)actual).isEmpty(); + return assertThat(Iterables.transform(actual, + new Function<ProjectInfo, Project.NameKey>() { + @Override + public Project.NameKey apply(ProjectInfo in) { + return new Project.NameKey(in.name); + } + })); } public static void assertProjectInfo(Project project, ProjectInfo info) { @@ -67,6 +75,6 @@ for (AccountGroup.UUID g : state.getOwners()) { assertThat(expectedOwners.remove(g)).isTrue(); } - assertThat((Iterable<?>)expectedOwners).isEmpty(); + assertThat(expectedOwners).isEmpty(); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java index 7e2af65..649534b 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
@@ -15,16 +15,15 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.checkout; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; import static com.google.gerrit.acceptance.GitUtil.fetch; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.project.ProjectState; -import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; import org.junit.Before; import org.junit.Test; @@ -32,8 +31,8 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest { @Before public void setUp() throws Exception { - fetch(git, RefNames.REFS_CONFIG + ":refs/heads/config"); - checkout(git, "refs/heads/config"); + fetch(testRepo, RefNames.REFS_CONFIG + ":refs/heads/config"); + testRepo.reset("refs/heads/config"); } @Test @@ -43,9 +42,9 @@ cfg.setString("s1", null, "k1", "v1"); cfg.setString("s2", "ss", "k2", "v2"); PushOneCommit push = - pushFactory.create(db, admin.getIdent(), "Create Project Level Config", + pushFactory.create(db, admin.getIdent(), testRepo, "Create Project Level Config", configName, cfg.toText()); - push.to(git, RefNames.REFS_CONFIG); + push.to(RefNames.REFS_CONFIG); ProjectState state = projectCache.get(project); assertThat(state.getConfig(configName).get().toText()).isEqualTo( @@ -68,23 +67,28 @@ parentCfg.setString("s2", "ss", "k3", "parentValue3"); parentCfg.setString("s2", "ss", "k4", "parentValue4"); - Git parentGit = - cloneProject(sshSession.getUrl() + "/" + allProjects.get(), false); - fetch(parentGit, RefNames.REFS_CONFIG + ":refs/heads/config"); - checkout(parentGit, "refs/heads/config"); - PushOneCommit push = - pushFactory.create(db, admin.getIdent(), "Create Project Level Config", - configName, parentCfg.toText()); - push.to(parentGit, RefNames.REFS_CONFIG); + pushFactory.create( + db, admin.getIdent(), testRepo, "Create Project Level Config", + configName, parentCfg.toText()) + .to(RefNames.REFS_CONFIG) + .assertOkStatus(); + + Project.NameKey childProject = createProject("child", project); + TestRepository<?> childTestRepo = cloneProject(childProject); + fetch(childTestRepo, RefNames.REFS_CONFIG + ":refs/heads/config"); + childTestRepo.reset("refs/heads/config"); Config cfg = new Config(); cfg.setString("s1", null, "k1", "childValue1"); cfg.setString("s2", "ss", "k3", "childValue2"); - push = pushFactory.create(db, admin.getIdent(), "Create Project Level Config", - configName, cfg.toText()); - push.to(git, RefNames.REFS_CONFIG); - ProjectState state = projectCache.get(project); + pushFactory.create( + db, admin.getIdent(), childTestRepo, "Create Project Level Config", + configName, cfg.toText()) + .to(RefNames.REFS_CONFIG) + .assertOkStatus(); + + ProjectState state = projectCache.get(childProject); Config expectedCfg = new Config(); expectedCfg.setString("s1", null, "k1", "childValue1");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java index ac90ac0..67d4d1f 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java
@@ -15,7 +15,6 @@ package com.google.gerrit.acceptance.rest.project; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.createProject; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.RestResponse; @@ -28,8 +27,7 @@ public class SetParentIT extends AbstractDaemonTest { @Test public void setParent_Forbidden() throws Exception { - String parent = "parent"; - createProject(sshSession, parent, null, true); + String parent = createProject("parent", null, true).get(); RestResponse r = userSession.put("/projects/" + project.get() + "/parent", newParentInput(parent)); @@ -39,8 +37,7 @@ @Test public void setParent() throws Exception { - String parent = "parent"; - createProject(sshSession, parent, null, true); + String parent = createProject("parent", null, true).get(); RestResponse r = adminSession.put("/projects/" + project.get() + "/parent", newParentInput(parent)); @@ -72,15 +69,13 @@ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); r.consume(); - String child = "child"; - createProject(sshSession, child, project, true); + Project.NameKey child = createProject("child", project, true); r = adminSession.put("/projects/" + project.get() + "/parent", - newParentInput(child)); + newParentInput(child.get())); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT); r.consume(); - String grandchild = "grandchild"; - createProject(sshSession, grandchild, new Project.NameKey(child), true); + String grandchild = createProject("grandchild", child, true).get(); r = adminSession.put("/projects/" + project.get() + "/parent", newParentInput(grandchild)); assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java index c6cf647..7efefa7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -50,16 +50,16 @@ grant(Permission.PUSH, project, "refs/tags/*"); PushOneCommit.Tag tag1 = new PushOneCommit.Tag("v1.0"); - PushOneCommit push1 = pushFactory.create(db, admin.getIdent()); + PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo); push1.setTag(tag1); - PushOneCommit.Result r1 = push1.to(git, "refs/for/master%submit"); + PushOneCommit.Result r1 = push1.to("refs/for/master%submit"); r1.assertOkStatus(); PushOneCommit.AnnotatedTag tag2 = new PushOneCommit.AnnotatedTag("v2.0", "annotation", admin.getIdent()); - PushOneCommit push2 = pushFactory.create(db, admin.getIdent()); + PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo); push2.setTag(tag2); - PushOneCommit.Result r2 = push2.to(git, "refs/for/master%submit"); + PushOneCommit.Result r2 = push2.to("refs/for/master%submit"); r2.assertOkStatus(); List<TagInfo> result = @@ -86,16 +86,16 @@ grant(Permission.PUSH, project, "refs/tags/*"); PushOneCommit.Tag tag1 = new PushOneCommit.Tag("v1.0"); - PushOneCommit push1 = pushFactory.create(db, admin.getIdent()); + PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo); push1.setTag(tag1); - PushOneCommit.Result r1 = push1.to(git, "refs/for/master%submit"); + PushOneCommit.Result r1 = push1.to("refs/for/master%submit"); r1.assertOkStatus(); pushTo("refs/heads/hidden"); PushOneCommit.Tag tag2 = new PushOneCommit.Tag("v2.0"); - PushOneCommit push2 = pushFactory.create(db, admin.getIdent()); + PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo); push2.setTag(tag2); - PushOneCommit.Result r2 = push2.to(git, "refs/for/hidden%submit"); + PushOneCommit.Result r2 = push2.to("refs/for/hidden%submit"); r2.assertOkStatus(); List<TagInfo> result = @@ -121,9 +121,9 @@ grant(Permission.PUSH, project, "refs/tags/*"); PushOneCommit.Tag tag1 = new PushOneCommit.Tag("v1.0"); - PushOneCommit push1 = pushFactory.create(db, admin.getIdent()); + PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo); push1.setTag(tag1); - PushOneCommit.Result r1 = push1.to(git, "refs/for/master%submit"); + PushOneCommit.Result r1 = push1.to("refs/for/master%submit"); r1.assertOkStatus(); RestResponse response =
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java index 541d1b8..721c712 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -15,13 +15,20 @@ package com.google.gerrit.acceptance.server.change; import static com.google.common.truth.Truth.assertThat; +import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME; +import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.extensions.api.changes.DraftInput; import com.google.gerrit.extensions.api.changes.ReviewInput; +import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput; +import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling; import com.google.gerrit.extensions.client.Comment; import com.google.gerrit.extensions.client.Side; import com.google.gerrit.extensions.common.CommentInfo; @@ -34,21 +41,21 @@ import com.google.gerrit.server.change.Revisions; import com.google.gerrit.server.notedb.NotesMigration; import com.google.gerrit.testutil.ConfigSuite; -import com.google.gson.reflect.TypeToken; +import com.google.gerrit.testutil.FakeEmailSender; +import com.google.gerrit.testutil.FakeEmailSender.Message; import com.google.inject.Inject; import com.google.inject.Provider; -import org.apache.http.HttpStatus; import org.eclipse.jgit.lib.Config; +import org.junit.Before; import org.junit.Test; -import java.io.IOException; -import java.lang.reflect.Type; import java.sql.Timestamp; import java.util.HashMap; import java.util.List; import java.util.Map; +@NoHttpd public class CommentsIT extends AbstractDaemonTest { @ConfigSuite.Config public static Config noteDbEnabled() { @@ -64,7 +71,15 @@ @Inject private Provider<PostReview> postReview; - private final Integer lines[] = {0, 1}; + @Inject + private FakeEmailSender email; + + private final Integer[] lines = {0, 1}; + + @Before + public void setUp() { + setApiUser(user); + } @Test public void createDraft() throws Exception { @@ -72,8 +87,7 @@ PushOneCommit.Result r = createChange(); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); - ReviewInput.CommentInput comment = newCommentInfo( - "file1", Side.REVISION, line, "comment 1"); + DraftInput comment = newDraft("file1", Side.REVISION, line, "comment 1"); addDraft(changeId, revId, comment); Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId); assertThat(result).hasSize(1); @@ -87,14 +101,13 @@ for (Integer line : lines) { String file = "file"; String contents = "contents " + line; - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + PushOneCommit.Result r = push.to("refs/for/master"); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); ReviewInput input = new ReviewInput(); - ReviewInput.CommentInput comment = newCommentInfo( - file, Side.REVISION, line, "comment 1"); + CommentInput comment = newComment(file, Side.REVISION, line, "comment 1"); input.comments = new HashMap<>(); input.comments.put(comment.path, Lists.newArrayList(comment)); revision(r).review(input); @@ -111,8 +124,7 @@ PushOneCommit.Result r = createChange(); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); - ReviewInput.CommentInput comment = newCommentInfo( - "file1", Side.REVISION, line, "comment 1"); + DraftInput comment = newDraft("file1", Side.REVISION, line, "comment 1"); addDraft(changeId, revId, comment); Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId); CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path)); @@ -132,7 +144,7 @@ PushOneCommit.Result r = createChange(); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); - ReviewInput.CommentInput comment = newCommentInfo( + DraftInput comment = newDraft( "file1", Side.REVISION, line, "comment 1"); CommentInfo returned = addDraft(changeId, revId, comment); CommentInfo actual = getDraftComment(changeId, revId, returned.id); @@ -146,9 +158,8 @@ PushOneCommit.Result r = createChange(); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); - ReviewInput.CommentInput comment = newCommentInfo( - "file1", Side.REVISION, line, "comment 1"); - CommentInfo returned = addDraft(changeId, revId, comment); + DraftInput draft = newDraft("file1", Side.REVISION, line, "comment 1"); + CommentInfo returned = addDraft(changeId, revId, draft); deleteDraft(changeId, revId, returned.id); Map<String, List<CommentInfo>> drafts = getDraftComments(changeId, revId); assertThat(drafts).isEmpty(); @@ -161,14 +172,13 @@ for (Integer line : lines) { String file = "file"; String contents = "contents " + line; - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + PushOneCommit.Result r = push.to("refs/for/master"); String changeId = r.getChangeId(); String revId = r.getCommit().getName(); ReviewInput input = new ReviewInput(); - ReviewInput.CommentInput comment = newCommentInfo( - file, Side.REVISION, line, "comment 1"); + CommentInput comment = newComment(file, Side.REVISION, line, "comment 1"); comment.updated = timestamp; input.comments = new HashMap<>(); input.comments.put(comment.path, Lists.newArrayList(comment)); @@ -186,56 +196,228 @@ } } - private CommentInfo addDraft(String changeId, String revId, - ReviewInput.CommentInput c) throws IOException { - RestResponse r = userSession.put( - "/changes/" + changeId + "/revisions/" + revId + "/drafts", c); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED); - return newGson().fromJson(r.getReader(), CommentInfo.class); + @Test + public void listChangeDrafts() throws Exception { + PushOneCommit.Result r1 = createChange(); + + PushOneCommit.Result r2 = pushFactory.create( + db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", + r1.getChangeId()) + .to("refs/for/master"); + + + setApiUser(admin); + addDraft(r1.getChangeId(), r1.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "nit: trailing whitespace")); + addDraft(r2.getChangeId(), r2.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "typo: content")); + + setApiUser(user); + addDraft(r2.getChangeId(), r2.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "+1, please fix")); + + setApiUser(admin); + Map<String, List<CommentInfo>> actual = + gApi.changes().id(r1.getChangeId()).drafts(); + assertThat((Iterable<?>) actual.keySet()).containsExactly(FILE_NAME); + List<CommentInfo> comments = actual.get(FILE_NAME); + assertThat(comments).hasSize(2); + + CommentInfo c1 = comments.get(0); + assertThat(c1.author).isNull(); + assertThat(c1.patchSet).isEqualTo(1); + assertThat(c1.message).isEqualTo("nit: trailing whitespace"); + assertThat(c1.side).isNull(); + assertThat(c1.line).isEqualTo(1); + + CommentInfo c2 = comments.get(1); + assertThat(c2.author).isNull(); + assertThat(c2.patchSet).isEqualTo(2); + assertThat(c2.message).isEqualTo("typo: content"); + assertThat(c2.side).isNull(); + assertThat(c2.line).isEqualTo(1); } - private void updateDraft(String changeId, String revId, - ReviewInput.CommentInput c, String uuid) throws IOException { - RestResponse r = userSession.put( - "/changes/" + changeId + "/revisions/" + revId + "/drafts/" + uuid, c); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); + @Test + public void listChangeComments() throws Exception { + PushOneCommit.Result r1 = createChange(); + + PushOneCommit.Result r2 = pushFactory.create( + db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", + r1.getChangeId()) + .to("refs/for/master"); + + addComment(r1, "nit: trailing whitespace"); + addComment(r2, "typo: content"); + + Map<String, List<CommentInfo>> actual = gApi.changes() + .id(r2.getChangeId()) + .comments(); + assertThat(actual.keySet()).containsExactly(FILE_NAME); + + List<CommentInfo> comments = actual.get(FILE_NAME); + assertThat(comments).hasSize(2); + + CommentInfo c1 = comments.get(0); + assertThat(c1.author._accountId).isEqualTo(user.getId().get()); + assertThat(c1.patchSet).isEqualTo(1); + assertThat(c1.message).isEqualTo("nit: trailing whitespace"); + assertThat(c1.side).isNull(); + assertThat(c1.line).isEqualTo(1); + + CommentInfo c2 = comments.get(1); + assertThat(c2.author._accountId).isEqualTo(user.getId().get()); + assertThat(c2.patchSet).isEqualTo(2); + assertThat(c2.message).isEqualTo("typo: content"); + assertThat(c2.side).isNull(); + assertThat(c2.line).isEqualTo(1); + } + + @Test + public void publishCommentsAllRevisions() throws Exception { + PushOneCommit.Result r1 = createChange(); + + PushOneCommit.Result r2 = pushFactory.create( + db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new\ncntent\n", + r1.getChangeId()) + .to("refs/for/master"); + + addDraft(r1.getChangeId(), r1.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "nit: trailing whitespace")); + addDraft(r2.getChangeId(), r2.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "join lines")); + addDraft(r2.getChangeId(), r2.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 2, "typo: content")); + + PushOneCommit.Result other = createChange(); + // Drafts on other changes aren't returned. + addDraft(other.getChangeId(), other.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 1, "unrelated comment")); + + setApiUser(admin); + // Drafts by other users aren't returned. + addDraft(r2.getChangeId(), r2.getCommit().getName(), + newDraft(FILE_NAME, Side.REVISION, 2, "oops")); + setApiUser(user); + + ReviewInput reviewInput = new ReviewInput(); + reviewInput.drafts = DraftHandling.PUBLISH_ALL_REVISIONS; + reviewInput.message = "comments"; + gApi.changes() + .id(r2.getChangeId()) + .current() + .review(reviewInput); + + assertThat(gApi.changes() + .id(r1.getChangeId()) + .revision(r1.getCommit().name()) + .drafts()) + .isEmpty(); + Map<String, List<CommentInfo>> ps1Map = gApi.changes() + .id(r1.getChangeId()) + .revision(r1.getCommit().name()) + .comments(); + assertThat(ps1Map.keySet()).containsExactly(FILE_NAME); + List<CommentInfo> ps1List = ps1Map.get(FILE_NAME); + assertThat(ps1List).hasSize(1); + assertThat(ps1List.get(0).message).isEqualTo("nit: trailing whitespace"); + + assertThat(gApi.changes() + .id(r2.getChangeId()) + .revision(r2.getCommit().name()) + .drafts()) + .isEmpty(); + Map<String, List<CommentInfo>> ps2Map = gApi.changes() + .id(r2.getChangeId()) + .revision(r2.getCommit().name()) + .comments(); + assertThat(ps2Map.keySet()).containsExactly(FILE_NAME); + List<CommentInfo> ps2List = ps2Map.get(FILE_NAME); + assertThat(ps2List).hasSize(2); + assertThat(ps2List.get(0).message).isEqualTo("join lines"); + assertThat(ps2List.get(1).message).isEqualTo("typo: content"); + + ImmutableList<Message> messages = + email.getMessages(r2.getChangeId(), "comment"); + assertThat(messages).hasSize(1); + String url = canonicalWebUrl.get(); + int c = r1.getChange().getId().get(); + assertThat(messages.get(0).body()).contains( + "\n" + + "Patch Set 2:\n" + + "\n" + + "(3 comments)\n" + + "\n" + + "comments\n" + + "\n" + + url + "#/c/" + c + "/1/a.txt\n" + + "File a.txt:\n" + + "\n" + + "PS1, Line 1: ew\n" + + "nit: trailing whitespace\n" + + "\n" + + "\n" + + url + "#/c/" + c + "/2/a.txt\n" + + "File a.txt:\n" + + "\n" + + "PS2, Line 1: ew\n" + + "join lines\n" + + "\n" + + "\n" + + "PS2, Line 2: nten\n" + + "typo: content\n" + + "\n" + + "\n" + + "-- \n"); + } + + + private void addComment(PushOneCommit.Result r, String message) + throws Exception { + CommentInput c = new CommentInput(); + c.line = 1; + c.message = message; + c.path = FILE_NAME; + ReviewInput in = new ReviewInput(); + in.comments = ImmutableMap.<String, List<CommentInput>> of( + FILE_NAME, ImmutableList.of(c)); + gApi.changes() + .id(r.getChangeId()) + .revision(r.getCommit().name()) + .review(in); + } + + private CommentInfo addDraft(String changeId, String revId, DraftInput in) + throws Exception { + return gApi.changes().id(changeId).revision(revId).createDraft(in).get(); + } + + private void updateDraft(String changeId, String revId, DraftInput in, + String uuid) throws Exception { + gApi.changes().id(changeId).revision(revId).draft(uuid).update(in); } private void deleteDraft(String changeId, String revId, String uuid) - throws IOException { - RestResponse r = userSession.delete( - "/changes/" + changeId + "/revisions/" + revId + "/drafts/" + uuid); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); + throws Exception { + gApi.changes().id(changeId).revision(revId).draft(uuid).delete(); } private Map<String, List<CommentInfo>> getPublishedComments(String changeId, - String revId) throws IOException { - RestResponse r = userSession.get( - "/changes/" + changeId + "/revisions/" + revId + "/comments/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Type mapType = new TypeToken<Map<String, List<CommentInfo>>>() {}.getType(); - return newGson().fromJson(r.getReader(), mapType); + String revId) throws Exception { + return gApi.changes().id(changeId).revision(revId).comments(); } private Map<String, List<CommentInfo>> getDraftComments(String changeId, - String revId) throws IOException { - RestResponse r = userSession.get( - "/changes/" + changeId + "/revisions/" + revId + "/drafts/"); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - Type mapType = new TypeToken<Map<String, List<CommentInfo>>>() {}.getType(); - return newGson().fromJson(r.getReader(), mapType); + String revId) throws Exception { + return gApi.changes().id(changeId).revision(revId).drafts(); } private CommentInfo getDraftComment(String changeId, String revId, - String uuid) throws IOException { - RestResponse r = userSession.get( - "/changes/" + changeId + "/revisions/" + revId + "/drafts/" + uuid); - assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK); - return newGson().fromJson(r.getReader(), CommentInfo.class); + String uuid) throws Exception { + return gApi.changes().id(changeId).revision(revId).draft(uuid).get(); } - private static void assertCommentInfo(ReviewInput.CommentInput expected, - CommentInfo actual) { + private static void assertCommentInfo(Comment expected, CommentInfo actual) { assertThat(actual.line).isEqualTo(expected.line); assertThat(actual.message).isEqualTo(expected.message); assertThat(actual.inReplyTo).isEqualTo(expected.inReplyTo); @@ -258,21 +440,32 @@ } } - private ReviewInput.CommentInput newCommentInfo(String path, - Side side, int line, String message) { - ReviewInput.CommentInput input = new ReviewInput.CommentInput(); - input.path = path; - input.side = side; - input.line = line != 0 ? line : null; - input.message = message; + private static CommentInput newComment(String path, Side side, int line, + String message) { + CommentInput c = new CommentInput(); + return populate(c, path, side, line, message); + } + + private DraftInput newDraft(String path, Side side, int line, + String message) { + DraftInput d = new DraftInput(); + return populate(d, path, side, line, message); + } + + private static <C extends Comment> C populate(C c, String path, Side side, + int line, String message) { + c.path = path; + c.side = side; + c.line = line != 0 ? line : null; + c.message = message; if (line != 0) { Comment.Range range = new Comment.Range(); - range.startLine = 1; + range.startLine = line; range.startCharacter = 1; - range.endLine = 1; + range.endLine = line; range.endCharacter = 5; - input.range = range; + c.range = range; } - return input; + return c; } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java index 6cd39ab..11acf00 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -15,16 +15,15 @@ package com.google.gerrit.acceptance.server.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.add; -import static com.google.gerrit.acceptance.GitUtil.createCommit; import static com.google.gerrit.acceptance.GitUtil.pushHead; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GitUtil.Commit; +import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.RestSession; +import com.google.gerrit.extensions.common.CommitInfo; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.change.GetRelated.ChangeAndCommit; @@ -35,7 +34,8 @@ import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; -import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; import java.io.IOException; @@ -50,140 +50,158 @@ @Test public void getRelatedNoResult() throws Exception { - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - PatchSet.Id ps = push.to(git, "refs/for/master").getPatchSetId(); - List<ChangeAndCommit> related = getRelated(ps); - assertThat(related).isEmpty(); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + assertRelated(push.to("refs/for/master").getPatchSetId()); } @Test public void getRelatedLinear() throws Exception { - add(git, "a.txt", "1"); - Commit c1 = createCommit(git, admin.getIdent(), "subject: 1"); - add(git, "b.txt", "2"); - Commit c2 = createCommit(git, admin.getIdent(), "subject: 2"); - pushHead(git, "refs/for/master", false); + RevCommit c1_1 = commitBuilder() + .add("a.txt", "1") + .message("subject: 1") + .create(); + String id1 = getChangeId(c1_1); + RevCommit c2_2 = commitBuilder() + .add("b.txt", "2") + .message("subject: 2") + .create(); + String id2 = getChangeId(c2_2); + pushHead(testRepo, "refs/for/master", false); - for (Commit c : ImmutableList.of(c2, c1)) { - List<ChangeAndCommit> related = getRelated(getPatchSetId(c)); - assertThat(related).hasSize(2); - assertThat(related.get(0).changeId) - .named("related to " + c.getChangeId()).isEqualTo(c2.getChangeId()); - assertThat(related.get(1).changeId) - .named("related to " + c.getChangeId()).isEqualTo(c1.getChangeId()); + for (RevCommit c : ImmutableList.of(c2_2, c1_1)) { + assertRelated(getPatchSetId(c), + changeAndCommit(id2, c2_2, 1, 1), + changeAndCommit(id1, c1_1, 1, 1)); } } @Test public void getRelatedReorder() throws Exception { // Create two commits and push. - add(git, "a.txt", "1"); - Commit c1 = createCommit(git, admin.getIdent(), "subject: 1"); - add(git, "b.txt", "2"); - Commit c2 = createCommit(git, admin.getIdent(), "subject: 2"); - pushHead(git, "refs/for/master", false); - PatchSet.Id c1ps1 = getPatchSetId(c1); - PatchSet.Id c2ps1 = getPatchSetId(c2); + RevCommit c1_1 = commitBuilder() + .add("a.txt", "1") + .message("subject: 1") + .create(); + String id1 = getChangeId(c1_1); + RevCommit c2_1 = commitBuilder() + .add("b.txt", "2") + .message("subject: 2") + .create(); + String id2 = getChangeId(c2_1); + pushHead(testRepo, "refs/for/master", false); + PatchSet.Id ps1_1 = getPatchSetId(c1_1); + PatchSet.Id ps2_1 = getPatchSetId(c2_1); // Swap the order of commits and push again. - git.reset().setMode(ResetType.HARD).setRef("HEAD^^").call(); - git.cherryPick().include(c2.getCommit()).include(c1.getCommit()).call(); - pushHead(git, "refs/for/master", false); - PatchSet.Id c1ps2 = getPatchSetId(c1); - PatchSet.Id c2ps2 = getPatchSetId(c2); + testRepo.reset("HEAD~2"); + RevCommit c2_2 = testRepo.cherryPick(c2_1); + RevCommit c1_2 = testRepo.cherryPick(c1_1); + pushHead(testRepo, "refs/for/master", false); + PatchSet.Id ps1_2 = getPatchSetId(c1_1); + PatchSet.Id ps2_2 = getPatchSetId(c2_1); - for (PatchSet.Id ps : ImmutableList.of(c2ps2, c1ps2)) { - List<ChangeAndCommit> related = getRelated(ps); - assertThat(related).hasSize(2); - assertThat(related.get(0).changeId).named("related to " + ps).isEqualTo( - c1.getChangeId()); - assertThat(related.get(1).changeId).named("related to " + ps).isEqualTo( - c2.getChangeId()); + for (PatchSet.Id ps : ImmutableList.of(ps2_2, ps1_2)) { + assertRelated(ps, + changeAndCommit(id1, c1_2, 2, 2), + changeAndCommit(id2, c2_2, 2, 2)); } - for (PatchSet.Id ps : ImmutableList.of(c2ps1, c1ps1)) { - List<ChangeAndCommit> related = getRelated(ps); - assertThat(related).hasSize(2); - assertThat(related.get(0).changeId).named("related to " + ps).isEqualTo( - c2.getChangeId()); - assertThat(related.get(1).changeId).named("related to " + ps).isEqualTo( - c1.getChangeId()); + for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps1_1)) { + assertRelated(ps, + changeAndCommit(id2, c2_1, 1, 2), + changeAndCommit(id1, c1_1, 1, 2)); } } @Test public void getRelatedReorderAndExtend() throws Exception { // Create two commits and push. - add(git, "a.txt", "1"); - Commit c1 = createCommit(git, admin.getIdent(), "subject: 1"); - add(git, "b.txt", "2"); - Commit c2 = createCommit(git, admin.getIdent(), "subject: 2"); - pushHead(git, "refs/for/master", false); - PatchSet.Id c1ps1 = getPatchSetId(c1); - PatchSet.Id c2ps1 = getPatchSetId(c2); + ObjectId initial = repo().getRef("HEAD").getObjectId(); + RevCommit c1_1 = commitBuilder() + .add("a.txt", "1") + .message("subject: 1") + .create(); + String id1 = getChangeId(c1_1); + RevCommit c2_1 = commitBuilder() + .add("b.txt", "2") + .message("subject: 2") + .create(); + String id2 = getChangeId(c2_1); + pushHead(testRepo, "refs/for/master", false); + PatchSet.Id ps1_1 = getPatchSetId(c1_1); + PatchSet.Id ps2_1 = getPatchSetId(c2_1); // Swap the order of commits, create a new commit on top, and push again. - git.reset().setMode(ResetType.HARD).setRef("HEAD^^").call(); - git.cherryPick().include(c2.getCommit()).include(c1.getCommit()).call(); - add(git, "c.txt", "3"); - Commit c3 = createCommit(git, admin.getIdent(), "subject: 3"); - pushHead(git, "refs/for/master", false); - PatchSet.Id c1ps2 = getPatchSetId(c1); - PatchSet.Id c2ps2 = getPatchSetId(c2); - PatchSet.Id c3ps1 = getPatchSetId(c3); + testRepo.reset(initial); + RevCommit c2_2 = testRepo.cherryPick(c2_1); + RevCommit c1_2 = testRepo.cherryPick(c1_1); + RevCommit c3_1 = commitBuilder() + .add("c.txt", "3") + .message("subject: 3") + .create(); + String id3 = getChangeId(c3_1); + pushHead(testRepo, "refs/for/master", false); + PatchSet.Id ps1_2 = getPatchSetId(c1_1); + PatchSet.Id ps2_2 = getPatchSetId(c2_1); + PatchSet.Id ps3_1 = getPatchSetId(c3_1); - - for (PatchSet.Id ps : ImmutableList.of(c3ps1, c2ps2, c1ps2)) { - List<ChangeAndCommit> related = getRelated(ps); - assertThat(related).hasSize(3); - assertThat(related.get(0).changeId).named("related to " + ps).isEqualTo( - c3.getChangeId()); - assertThat(related.get(1).changeId).named("related to " + ps).isEqualTo( - c1.getChangeId()); - assertThat(related.get(2).changeId).named("related to " + ps).isEqualTo( - c2.getChangeId()); + for (PatchSet.Id ps : ImmutableList.of(ps3_1, ps2_2, ps1_2)) { + assertRelated(ps, + changeAndCommit(id3, c3_1, 1, 1), + changeAndCommit(id1, c1_2, 2, 2), + changeAndCommit(id2, c2_2, 2, 2)); } - for (PatchSet.Id ps : ImmutableList.of(c2ps1, c1ps1)) { - List<ChangeAndCommit> related = getRelated(ps); - assertThat(related).hasSize(3); - assertThat(related.get(0).changeId).named("related to " + ps).isEqualTo( - c3.getChangeId()); - assertThat(related.get(1).changeId).named("related to " + ps).isEqualTo( - c2.getChangeId()); - assertThat(related.get(2).changeId).named("related to " + ps).isEqualTo( - c1.getChangeId()); + for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps1_1)) { + assertRelated(ps, + changeAndCommit(id3, c3_1, 1, 1), + changeAndCommit(id2, c2_1, 1, 2), + changeAndCommit(id1, c1_1, 1, 2)); } } @Test public void getRelatedEdit() throws Exception { - add(git, "a.txt", "1"); - Commit c1 = createCommit(git, admin.getIdent(), "subject: 1"); - add(git, "b.txt", "2"); - Commit c2 = createCommit(git, admin.getIdent(), "subject: 2"); - add(git, "b.txt", "3"); - Commit c3 = createCommit(git, admin.getIdent(), "subject: 3"); - pushHead(git, "refs/for/master", false); + RevCommit c1_1 = commitBuilder() + .add("a.txt", "1") + .message("subject: 1") + .create(); + String id1 = getChangeId(c1_1); + RevCommit c2_1 = commitBuilder() + .add("b.txt", "2") + .message("subject: 2") + .create(); + String id2 = getChangeId(c2_1); + RevCommit c3_1 = commitBuilder() + .add("c.txt", "3") + .message("subject: 3") + .create(); + String id3 = getChangeId(c3_1); + pushHead(testRepo, "refs/for/master", false); - Change ch2 = getChange(c2).change(); + Change ch2 = getChange(c2_1).change(); editModifier.createEdit(ch2, getPatchSet(ch2)); editModifier.modifyFile(editUtil.byChange(ch2).get(), "a.txt", RestSession.newRawInput(new byte[] {'a'})); - String editRev = editUtil.byChange(ch2).get().getRevision().get(); + ObjectId editRev = + ObjectId.fromString(editUtil.byChange(ch2).get().getRevision().get()); - List<ChangeAndCommit> related = getRelated(ch2.getId(), 0); - assertThat(related).hasSize(3); - assertThat(related.get(0).changeId).named("related to " + c2.getChangeId()) - .isEqualTo(c3.getChangeId()); - assertThat(related.get(1).changeId).named("related to " + c2.getChangeId()) - .isEqualTo(c2.getChangeId()); - assertThat(related.get(1)._revisionNumber.intValue()).named( - "has edit revision number").isEqualTo(0); - assertThat(related.get(1).commit.commit).named( - "has edit revision " + editRev).isEqualTo(editRev); - assertThat(related.get(2).changeId).named("related to " + c2.getChangeId()) - .isEqualTo(c1.getChangeId()); + PatchSet.Id ps1_1 = getPatchSetId(c1_1); + PatchSet.Id ps2_1 = getPatchSetId(c2_1); + PatchSet.Id ps2_edit = new PatchSet.Id(ch2.getId(), 0); + PatchSet.Id ps3_1 = getPatchSetId(c3_1); + + for (PatchSet.Id ps : ImmutableList.of(ps1_1, ps2_1, ps3_1)) { + assertRelated(ps, + changeAndCommit(id3, c3_1, 1, 1), + changeAndCommit(id2, c2_1, 1, 1), + changeAndCommit(id1, c1_1, 1, 1)); + } + + assertRelated(ps2_edit, + changeAndCommit(id3, c3_1, 1, 1), + changeAndCommit(id2, editRev, 0, 1), + changeAndCommit(id1, c1_1, 1, 1)); } private List<ChangeAndCommit> getRelated(PatchSet.Id ps) throws IOException { @@ -198,7 +216,11 @@ RelatedInfo.class).changes; } - private PatchSet.Id getPatchSetId(Commit c) throws OrmException { + private String getChangeId(RevCommit c) throws Exception { + return GitUtil.getChangeId(testRepo, c).get(); + } + + private PatchSet.Id getPatchSetId(ObjectId c) throws OrmException { return getChange(c).change().currentPatchSetId(); } @@ -206,8 +228,38 @@ return db.patchSets().get(c.currentPatchSetId()); } - private ChangeData getChange(Commit c) throws OrmException { - return Iterables.getOnlyElement( - queryProvider.get().byKeyPrefix(c.getChangeId())); + private ChangeData getChange(ObjectId c) throws OrmException { + return Iterables.getOnlyElement(queryProvider.get().byCommit(c)); + } + + private static ChangeAndCommit changeAndCommit(String changeId, + ObjectId commitId, int revisionNum, int currentRevisionNum) { + ChangeAndCommit result = new ChangeAndCommit(); + result.changeId = changeId; + result.commit = new CommitInfo(); + result.commit.commit = commitId.name(); + result._revisionNumber = revisionNum; + result._currentRevisionNumber = currentRevisionNum; + return result; + } + + private void assertRelated(PatchSet.Id psId, ChangeAndCommit... expected) + throws Exception { + List<ChangeAndCommit> actual = getRelated(psId); + assertThat(actual).hasSize(expected.length); + for (int i = 0; i < actual.size(); i++) { + String name = "index " + i + " related to " + psId; + ChangeAndCommit a = actual.get(i); + ChangeAndCommit e = expected[i]; + assertThat(a.changeId).named("Change-Id of " + name) + .isEqualTo(e.changeId); + assertThat(a.commit.commit).named("commit of " + name) + .isEqualTo(e.commit.commit); + // Don't bother checking _changeNumber; assume changeId is sufficient. + assertThat(a._revisionNumber).named("revision of " + name) + .isEqualTo(e._revisionNumber); + assertThat(a._currentRevisionNumber).named("current revision of " + name) + .isEqualTo(e._currentRevisionNumber); + } } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java index d598b06..57b00bc 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
@@ -15,14 +15,10 @@ package com.google.gerrit.acceptance.server.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.add; -import static com.google.gerrit.acceptance.GitUtil.amendCommit; -import static com.google.gerrit.acceptance.GitUtil.createCommit; +import static com.google.gerrit.acceptance.GitUtil.getChangeId; import static com.google.gerrit.acceptance.GitUtil.pushHead; -import static com.google.gerrit.acceptance.GitUtil.rm; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GitUtil.Commit; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace; @@ -34,8 +30,8 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException; import com.google.inject.Inject; -import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; import java.util.List; @@ -55,28 +51,35 @@ @Test public void listPatchesAgainstBase() throws Exception { - add(git, FILE_D, "4"); - createCommit(git, admin.getIdent(), SUBJECT_1); - pushHead(git, "refs/heads/master", false); + commitBuilder() + .add(FILE_D, "4") + .message(SUBJECT_1) + .create(); + pushHead(testRepo, "refs/heads/master", false); // Change 1, 1 (+FILE_A, -FILE_D) - add(git, FILE_A, "1"); - rm(git, FILE_D); - Commit c = createCommit(git, admin.getIdent(), SUBJECT_2); - pushHead(git, "refs/for/master", false); + RevCommit c = commitBuilder() + .add(FILE_A, "1") + .rm(FILE_D) + .message(SUBJECT_2) + .insertChangeId() + .create(); + String id = getChangeId(testRepo, c).get(); + pushHead(testRepo, "refs/for/master", false); // Compare Change 1,1 with Base (+FILE_A, -FILE_D) - List<PatchListEntry> entries = getCurrentPatches(c.getChangeId()); + List<PatchListEntry> entries = getCurrentPatches(id); assertThat(entries).hasSize(3); assertAdded(Patch.COMMIT_MSG, entries.get(0)); assertAdded(FILE_A, entries.get(1)); assertDeleted(FILE_D, entries.get(2)); // Change 1,2 (+FILE_A, +FILE_B, -FILE_D) - add(git, FILE_B, "2"); - c = amendCommit(git, admin.getIdent(), SUBJECT_2, c.getChangeId()); - pushHead(git, "refs/for/master", false); - entries = getCurrentPatches(c.getChangeId()); + c = amendBuilder() + .add(FILE_B, "2") + .create(); + pushHead(testRepo, "refs/for/master", false); + entries = getCurrentPatches(id); // Compare Change 1,2 with Base (+FILE_A, +FILE_B, -FILE_D) assertThat(entries).hasSize(4); @@ -88,33 +91,40 @@ @Test public void listPatchesAgainstBaseWithRebase() throws Exception { - add(git, FILE_D, "4"); - createCommit(git, admin.getIdent(), SUBJECT_1); - pushHead(git, "refs/heads/master", false); + commitBuilder() + .add(FILE_D, "4") + .message(SUBJECT_1) + .create(); + pushHead(testRepo, "refs/heads/master", false); // Change 1,1 (+FILE_A, -FILE_D) - add(git, FILE_A, "1"); - rm(git, FILE_D); - Commit c = createCommit(git, admin.getIdent(), SUBJECT_2); - pushHead(git, "refs/for/master", false); - List<PatchListEntry> entries = getCurrentPatches(c.getChangeId()); + RevCommit c = commitBuilder() + .add(FILE_A, "1") + .rm(FILE_D) + .message(SUBJECT_2) + .create(); + String id = getChangeId(testRepo, c).get(); + pushHead(testRepo, "refs/for/master", false); + List<PatchListEntry> entries = getCurrentPatches(id); assertThat(entries).hasSize(3); assertAdded(Patch.COMMIT_MSG, entries.get(0)); assertAdded(FILE_A, entries.get(1)); assertDeleted(FILE_D, entries.get(2)); // Change 2,1 (+FILE_B) - git.reset().setMode(ResetType.HARD).setRef("HEAD~1").call(); - add(git, FILE_B, "2"); - createCommit(git, admin.getIdent(), SUBJECT_3); - pushHead(git, "refs/for/master", false); + testRepo.reset("HEAD~1"); + commitBuilder() + .add(FILE_B, "2") + .message(SUBJECT_3) + .create(); + pushHead(testRepo, "refs/for/master", false); // Change 1,2 (+FILE_A, -FILE_D)) - git.cherryPick().include(c.getCommit()).call(); - pushHead(git, "refs/for/master", false); + testRepo.cherryPick(c); + pushHead(testRepo, "refs/for/master", false); // Compare Change 1,2 with Base (+FILE_A, -FILE_D)) - entries = getCurrentPatches(c.getChangeId()); + entries = getCurrentPatches(id); assertThat(entries).hasSize(3); assertAdded(Patch.COMMIT_MSG, entries.get(0)); assertAdded(FILE_A, entries.get(1)); @@ -123,24 +133,27 @@ @Test public void listPatchesAgainstOtherPatchSet() throws Exception { - add(git, FILE_D, "4"); - createCommit(git, admin.getIdent(), SUBJECT_1); - pushHead(git, "refs/heads/master", false); + commitBuilder() + .add(FILE_D, "4") + .message(SUBJECT_1) + .create(); + pushHead(testRepo, "refs/heads/master", false); // Change 1,1 (+FILE_A, +FILE_C, -FILE_D) - add(git, FILE_A, "1"); - add(git, FILE_C, "3"); - rm(git, FILE_D); - Commit c = createCommit(git, admin.getIdent(), SUBJECT_2); - pushHead(git, "refs/for/master", false); - ObjectId a = getCurrentRevisionId(c.getChangeId()); + RevCommit a = commitBuilder() + .add(FILE_A, "1") + .add(FILE_C, "3") + .rm(FILE_D) + .message(SUBJECT_2) + .create(); + pushHead(testRepo, "refs/for/master", false); // Change 1,2 (+FILE_A, +FILE_B, -FILE_D) - add(git, FILE_B, "2"); - rm(git, FILE_C); - c = amendCommit(git, admin.getIdent(), SUBJECT_2, c.getChangeId()); - pushHead(git, "refs/for/master", false); - ObjectId b = getCurrentRevisionId(c.getChangeId()); + RevCommit b = amendBuilder() + .add(FILE_B, "2") + .rm(FILE_C) + .create(); + pushHead(testRepo, "refs/for/master", false); // Compare Change 1,1 with Change 1,2 (+FILE_B) List<PatchListEntry> entries = getPatches(a, b); @@ -151,29 +164,34 @@ @Test public void listPatchesAgainstOtherPatchSetWithRebase() throws Exception { - add(git, FILE_D, "4"); - createCommit(git, admin.getIdent(), SUBJECT_1); - pushHead(git, "refs/heads/master", false); + commitBuilder() + .add(FILE_D, "4") + .message(SUBJECT_1) + .create(); + pushHead(testRepo, "refs/heads/master", false); // Change 1,1 (+FILE_A, -FILE_D) - add(git, FILE_A, "1"); - rm(git, FILE_D); - Commit c = createCommit(git, admin.getIdent(), SUBJECT_2); - pushHead(git, "refs/for/master", false); - ObjectId a = getCurrentRevisionId(c.getChangeId()); + RevCommit a = commitBuilder() + .add(FILE_A, "1") + .rm(FILE_D) + .message(SUBJECT_2) + .create(); + pushHead(testRepo, "refs/for/master", false); // Change 2,1 (+FILE_B) - git.reset().setMode(ResetType.HARD).setRef("HEAD~1").call(); - add(git, FILE_B, "2"); - createCommit(git, admin.getIdent(), SUBJECT_3); - pushHead(git, "refs/for/master", false); + testRepo.reset("HEAD~1"); + commitBuilder() + .add(FILE_B, "2") + .message(SUBJECT_3) + .create(); + pushHead(testRepo, "refs/for/master", false); // Change 1,2 (+FILE_A, +FILE_C, -FILE_D) - git.cherryPick().include(c.getCommit()).call(); - add(git, FILE_C, "2"); - c = amendCommit(git, admin.getIdent(), SUBJECT_2, c.getChangeId()); - pushHead(git, "refs/for/master", false); - ObjectId b = getCurrentRevisionId(c.getChangeId()); + testRepo.cherryPick(a); + RevCommit b = amendBuilder() + .add(FILE_C, "2") + .create(); + pushHead(testRepo, "refs/for/master", false); // Compare Change 1,1 with Change 1,2 (+FILE_C) List<PatchListEntry> entries = getPatches(a, b);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java index cd76c12..da83157 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -28,7 +28,6 @@ import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.LabelInfo; import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.project.Util; @@ -39,29 +38,29 @@ @NoHttpd public class CustomLabelIT extends AbstractDaemonTest { - private final LabelType Q = category("CustomLabel", + private final LabelType label = category("CustomLabel", value(1, "Positive"), value(0, "No score"), value(-1, "Negative")); @Before public void setUp() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); AccountGroup.UUID anonymousUsers = SystemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID(); - Util.allow(cfg, Permission.forLabel(Q.getName()), -1, 1, anonymousUsers, + Util.allow(cfg, Permission.forLabel(label.getName()), -1, 1, anonymousUsers, "refs/heads/*"); - saveProjectConfig(cfg); + saveProjectConfig(project, cfg); } @Test public void customLabelNoOp_NegativeVoteNotBlock() throws Exception { - Q.setFunctionName("NoOp"); + label.setFunctionName("NoOp"); saveLabelConfig(); PushOneCommit.Result r = createChange(); - revision(r).review(new ReviewInput().label(Q.getName(), -1)); + revision(r).review(new ReviewInput().label(label.getName(), -1)); ChangeInfo c = get(r.getChangeId()); - LabelInfo q = c.labels.get(Q.getName()); + LabelInfo q = c.labels.get(label.getName()); assertThat(q.all).hasSize(1); assertThat(q.rejected).isNotNull(); assertThat(q.blocking).isNull(); @@ -69,12 +68,12 @@ @Test public void customLabelNoBlock_NegativeVoteNotBlock() throws Exception { - Q.setFunctionName("NoBlock"); + label.setFunctionName("NoBlock"); saveLabelConfig(); PushOneCommit.Result r = createChange(); - revision(r).review(new ReviewInput().label(Q.getName(), -1)); + revision(r).review(new ReviewInput().label(label.getName(), -1)); ChangeInfo c = get(r.getChangeId()); - LabelInfo q = c.labels.get(Q.getName()); + LabelInfo q = c.labels.get(label.getName()); assertThat(q.all).hasSize(1); assertThat(q.rejected).isNotNull(); assertThat(q.blocking).isNull(); @@ -82,12 +81,12 @@ @Test public void customLabelMaxNoBlock_NegativeVoteNotBlock() throws Exception { - Q.setFunctionName("MaxNoBlock"); + label.setFunctionName("MaxNoBlock"); saveLabelConfig(); PushOneCommit.Result r = createChange(); - revision(r).review(new ReviewInput().label(Q.getName(), -1)); + revision(r).review(new ReviewInput().label(label.getName(), -1)); ChangeInfo c = get(r.getChangeId()); - LabelInfo q = c.labels.get(Q.getName()); + LabelInfo q = c.labels.get(label.getName()); assertThat(q.all).hasSize(1); assertThat(q.rejected).isNotNull(); assertThat(q.blocking).isNull(); @@ -95,12 +94,12 @@ @Test public void customLabelAnyWithBlock_NegativeVoteBlock() throws Exception { - Q.setFunctionName("AnyWithBlock"); + label.setFunctionName("AnyWithBlock"); saveLabelConfig(); PushOneCommit.Result r = createChange(); - revision(r).review(new ReviewInput().label(Q.getName(), -1)); + revision(r).review(new ReviewInput().label(label.getName(), -1)); ChangeInfo c = get(r.getChangeId()); - LabelInfo q = c.labels.get(Q.getName()); + LabelInfo q = c.labels.get(label.getName()); assertThat(q.all).hasSize(1); assertThat(q.disliked).isNull(); assertThat(q.rejected).isNotNull(); @@ -111,9 +110,9 @@ public void customLabelMaxWithBlock_NegativeVoteBlock() throws Exception { saveLabelConfig(); PushOneCommit.Result r = createChange(); - revision(r).review(new ReviewInput().label(Q.getName(), -1)); + revision(r).review(new ReviewInput().label(label.getName(), -1)); ChangeInfo c = get(r.getChangeId()); - LabelInfo q = c.labels.get(Q.getName()); + LabelInfo q = c.labels.get(label.getName()); assertThat(q.all).hasSize(1); assertThat(q.disliked).isNull(); assertThat(q.rejected).isNotNull(); @@ -121,18 +120,8 @@ } private void saveLabelConfig() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - cfg.getLabelSections().put(Q.getName(), Q); - saveProjectConfig(cfg); - } - - private void saveProjectConfig(ProjectConfig cfg) throws Exception { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); - try { - cfg.commit(md); - } finally { - md.close(); - } - projectCache.evict(allProjects); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + cfg.getLabelSections().put(label.getName(), label); + saveProjectConfig(project, cfg); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java index efb4615..9d44ad6 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
@@ -14,7 +14,6 @@ package com.google.gerrit.acceptance.server.project; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; import com.google.gerrit.acceptance.AbstractDaemonTest; @@ -28,6 +27,7 @@ import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.notedb.NotesMigration; +import com.google.gerrit.server.project.Util; import com.google.gerrit.testutil.ConfigSuite; import org.eclipse.jgit.lib.Config; @@ -46,15 +46,15 @@ @Before public void setUp() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); - codeReview = checkNotNull(cfg.getLabelSections().get("Code-Review")); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + codeReview = Util.codeReview(); codeReview.setDefaultValue((short)-1); + cfg.getLabelSections().put(codeReview.getName(), codeReview); saveProjectConfig(cfg); } @Test public void noCopyMinScoreOnRework() throws Exception { - //allProjects only has it true by default codeReview.setCopyMinScore(false); saveLabelConfig(); @@ -143,15 +143,15 @@ String file = "a.txt"; String contents = "contents"; - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + PushOneCommit.Result r = push.to("refs/for/master"); revision(r).review(ReviewInput.recommend()); assertApproval(r, 1); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, "second subject", file, contents, r.getChangeId()); - r = push.to(git, "refs/for/master"); + r = push.to("refs/for/master"); assertApproval(r, 0); } @@ -162,15 +162,15 @@ codeReview.setCopyAllScoresIfNoCodeChange(true); saveLabelConfig(); - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents); - PushOneCommit.Result r = push.to(git, "refs/for/master"); + PushOneCommit.Result r = push.to("refs/for/master"); revision(r).review(ReviewInput.recommend()); assertApproval(r, 1); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, "second subject", file, contents, r.getChangeId()); - r = push.to(git, "refs/for/master"); + r = push.to("refs/for/master"); assertApproval(r, 1); } @@ -180,18 +180,18 @@ String file = "a.txt"; String contents = "contents"; - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r1 = push.to(git, "refs/for/master"); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r1 = push.to("refs/for/master"); merge(r1); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, "non-conflicting", "b.txt", "other contents"); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push.to("refs/for/master"); merge(r2); - git.checkout().setName(r1.getCommit().name()).call(); - push = pushFactory.create(db, admin.getIdent(), subject, file, contents); - PushOneCommit.Result r3 = push.to(git, "refs/for/master"); + testRepo.reset(r1.getCommit()); + push = pushFactory.create(db, admin.getIdent(), testRepo, subject, file, contents); + PushOneCommit.Result r3 = push.to("refs/for/master"); revision(r3).review(ReviewInput.recommend()); assertApproval(r3, 1); @@ -207,18 +207,18 @@ codeReview.setCopyAllScoresOnTrivialRebase(true); saveLabelConfig(); - PushOneCommit push = pushFactory.create(db, admin.getIdent()); - PushOneCommit.Result r1 = push.to(git, "refs/for/master"); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + PushOneCommit.Result r1 = push.to("refs/for/master"); merge(r1); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, "non-conflicting", "b.txt", "other contents"); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push.to("refs/for/master"); merge(r2); - git.checkout().setName(r1.getCommit().name()).call(); - push = pushFactory.create(db, admin.getIdent(), subject, file, contents); - PushOneCommit.Result r3 = push.to(git, "refs/for/master"); + testRepo.reset(r1.getCommit()); + push = pushFactory.create(db, admin.getIdent(), testRepo, subject, file, contents); + PushOneCommit.Result r3 = push.to("refs/for/master"); revision(r3).review(ReviewInput.recommend()); assertApproval(r3, 1); @@ -232,11 +232,11 @@ saveLabelConfig(); PushOneCommit.Result r1 = createChange(); - git.checkout().setName(r1.getCommit().name()).call(); + testRepo.reset(r1.getCommit()); - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "other contents"); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push.to("refs/for/master"); revision(r2).review(ReviewInput.recommend()); @@ -262,11 +262,11 @@ PushOneCommit.Result r1 = createChange(); - git.checkout().setName(r1.getCommit().name()).call(); + testRepo.reset(r1.getCommit()); - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "other contents"); - PushOneCommit.Result r2 = push.to(git, "refs/for/master"); + PushOneCommit.Result r2 = push.to("refs/for/master"); revision(r2).review(ReviewInput.recommend()); @@ -288,38 +288,38 @@ String file = "a.txt"; String contents = "contents"; - PushOneCommit push = pushFactory.create(db, admin.getIdent(), + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, file, contents); - PushOneCommit.Result base = push.to(git, "refs/for/master"); + PushOneCommit.Result base = push.to("refs/for/master"); merge(base); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, file, contents + "M"); - PushOneCommit.Result basePlusM = push.to(git, "refs/for/master"); + PushOneCommit.Result basePlusM = push.to("refs/for/master"); merge(basePlusM); - push = pushFactory.create(db, admin.getIdent(), + push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, file, contents); - PushOneCommit.Result basePlusMMinusM = push.to(git, "refs/for/master"); + PushOneCommit.Result basePlusMMinusM = push.to("refs/for/master"); merge(basePlusMMinusM); - git.checkout().setName(base.getCommit().name()).call(); - push = pushFactory.create(db, admin.getIdent(), + testRepo.reset(base.getCommit()); + push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, file, contents + "MM"); - PushOneCommit.Result patchSet = push.to(git, "refs/for/master"); + PushOneCommit.Result patchSet = push.to("refs/for/master"); revision(patchSet).review(ReviewInput.recommend()); return patchSet; } private void saveLabelConfig() throws Exception { - ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig(); + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); cfg.getLabelSections().clear(); cfg.getLabelSections().put(codeReview.getName(), codeReview); saveProjectConfig(cfg); } private void saveProjectConfig(ProjectConfig cfg) throws Exception { - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); + MetaDataUpdate md = metaDataUpdateFactory.create(project); try { cfg.commit(md); } finally { @@ -356,6 +356,6 @@ assertThat((int) cr.defaultValue).isEqualTo(-1); assertThat(cr.all).hasSize(1); assertThat(cr.all.get(0).name).isEqualTo("Administrator"); - assertThat(cr.all.get(0).value.intValue()).isEqualTo(expected); + assertThat(cr.all.get(0).value).isEqualTo(expected); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java new file mode 100644 index 0000000..bab8e33 --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -0,0 +1,78 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.server.project; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; +import com.google.gerrit.server.git.NotifyConfig; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.mail.Address; +import com.google.gerrit.testutil.FakeEmailSender; +import com.google.gerrit.testutil.FakeEmailSender.Message; +import com.google.inject.Inject; + +import org.junit.Test; + +import java.util.EnumSet; +import java.util.List; + +@NoHttpd +public class ProjectWatchIT extends AbstractDaemonTest { + @Inject + private FakeEmailSender sender; + + @Test + public void newPatchSetsNotifyConfig() throws Exception { + Address addr = new Address("Watcher", "watcher@example.com"); + NotifyConfig nc = new NotifyConfig(); + nc.addEmail(addr); + nc.setName("new-patch-set"); + nc.setHeader(NotifyConfig.Header.CC); + nc.setTypes(EnumSet.of(NotifyType.NEW_PATCHSETS)); + nc.setFilter("message:sekret"); + + ProjectConfig cfg = projectCache.checkedGet(project).getConfig(); + cfg.putNotifyConfig("watch", nc); + saveProjectConfig(project, cfg); + + PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), testRepo, + "original subject", "a", "a1") + .to("refs/for/master"); + r.assertOkStatus(); + + r = pushFactory.create(db, admin.getIdent(), testRepo, + "super sekret subject", "a", "a2", r.getChangeId()) + .to("refs/for/master"); + r.assertOkStatus(); + + r = pushFactory.create(db, admin.getIdent(), testRepo, + "back to original subject", "a", "a3") + .to("refs/for/master"); + r.assertOkStatus(); + + List<Message> messages = sender.getMessages(); + assertThat(messages).hasSize(1); + Message m = messages.get(0); + assertThat(m.rcpt()).containsExactly(addr); + assertThat(m.body()).contains("Change subject: super sekret subject\n"); + assertThat(m.body()).contains("Gerrit-PatchSet: 2\n"); + } + + // TODO(anybody reading this): More tests. +}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK index 2ea5dec..d067b34 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BUCK
@@ -2,5 +2,6 @@ acceptance_tests( srcs = glob(['*IT.java']), + deps = ['//lib/commons:compress'], labels = ['ssh'], )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java index e483716..3bef84b 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
@@ -16,14 +16,12 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assert_; -import static com.google.gerrit.acceptance.GitUtil.add; -import static com.google.gerrit.acceptance.GitUtil.createCommit; import static com.google.gerrit.acceptance.GitUtil.pushHead; import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GitUtil.Commit; import com.google.gerrit.acceptance.NoHttpd; +import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.PushResult; import org.junit.Test; @@ -34,17 +32,17 @@ @Test public void banCommit() throws Exception { - add(git, "a.txt", "some content"); - Commit c = createCommit(git, admin.getIdent(), "subject"); + RevCommit c = commitBuilder() + .add("a.txt", "some content") + .create(); String response = - sshSession.exec("gerrit ban-commit " + project.get() + " " - + c.getCommit().getName()); + sshSession.exec("gerrit ban-commit " + project.get() + " " + c.name()); assert_().withFailureMessage(sshSession.getError()) .that(sshSession.hasError()).isFalse(); assertThat(response.toLowerCase(Locale.US)).doesNotContain("error"); - PushResult pushResult = pushHead(git, "refs/heads/master", false); + PushResult pushResult = pushHead(testRepo, "refs/heads/master", false); assertThat(pushResult.getRemoteUpdate("refs/heads/master").getMessage()) .startsWith("contains banned commit"); }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java index 2bdd894..4f9f190 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -16,7 +16,6 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assert_; -import static com.google.gerrit.acceptance.GitUtil.createProject; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GcAssert; @@ -52,11 +51,8 @@ @Before public void setUp() throws Exception { - project2 = new Project.NameKey("p2"); - createProject(sshSession, project2.get()); - - project3 = new Project.NameKey("p3"); - createProject(sshSession, project3.get()); + project2 = createProject("p2"); + project3 = createProject("p3"); } @Test @@ -97,7 +93,7 @@ GarbageCollectionResult result = garbageCollectionFactory.create().run( Arrays.asList(allProjects, project, project2, project3)); assertThat(result.hasErrors()).isTrue(); - assertThat(result.getErrors().size()).isEqualTo(1); + assertThat(result.getErrors()).hasSize(1); GarbageCollectionResult.Error error = result.getErrors().get(0); assertThat(error.getType()).isEqualTo( GarbageCollectionResult.Error.Type.GC_ALREADY_SCHEDULED);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/JschVerifyFalseBugIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/JschVerifyFalseBugIT.java index c9e0a89..7fb99b6 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/JschVerifyFalseBugIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/JschVerifyFalseBugIT.java
@@ -15,11 +15,11 @@ package com.google.gerrit.acceptance.ssh; import static com.google.common.truth.Truth.assertThat; -import static com.google.gerrit.acceptance.GitUtil.cloneProject; -import static com.google.gerrit.acceptance.GitUtil.createProject; import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.reviewdb.client.Project; import org.junit.Ignore; import org.junit.Test; @@ -50,8 +50,8 @@ public Void call() throws Exception { for (int i = 1; i < 100; i++) { String p = "p" + i; - createProject(sshSession, p); - cloneProject(sshSession.getUrl() + "/" + p); + createProject(p); + GitUtil.cloneProject(new Project.NameKey(p), sshSession); } return null; } @@ -62,6 +62,6 @@ for (Future<Void> future : futures) { future.get(); } - assertThat(futures.size()).isEqualTo(threads); + assertThat(futures).hasSize(threads); } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java new file mode 100644 index 0000000..88821ce --- /dev/null +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
@@ -0,0 +1,127 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.acceptance.ssh; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.GerritConfig; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.PushOneCommit; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.eclipse.jgit.transport.PacketLineIn; +import org.eclipse.jgit.transport.PacketLineOut; +import org.eclipse.jgit.util.IO; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Set; +import java.util.TreeSet; + +@NoHttpd +public class UploadArchiveIT extends AbstractDaemonTest { + + @Test + @GerritConfig(name = "download.archive", value = "off") + public void archiveFeatureOff() throws Exception { + archiveNotPermitted(); + } + + @Test + @GerritConfig(name = "download.archive", values = {"tar", "tbz2", "tgz", "txz"}) + public void zipFormatDisabled() throws Exception { + archiveNotPermitted(); + } + + @Test + public void zipFormat() throws Exception { + PushOneCommit.Result r = createChange(); + String abbreviated = r.getCommitId().abbreviate(8).name(); + String c = command(r, abbreviated); + + InputStream out = + sshSession.exec2("git-upload-archive " + project.get(), + argumentsToInputStream(c)); + + // Wrap with PacketLineIn to read ACK bytes from output stream + PacketLineIn in = new PacketLineIn(out); + String tmp = in.readString(); + assertThat(tmp).isEqualTo("ACK"); + tmp = in.readString(); + + // Skip length (4 bytes) + 1 byte + // to position the output stream to the raw zip stream + byte[] buffer = new byte[5]; + IO.readFully(out, buffer, 0, 5); + Set<String> entryNames = new TreeSet<>(); + try (ZipArchiveInputStream zip = new ZipArchiveInputStream(out)) { + ZipArchiveEntry zipEntry = zip.getNextZipEntry(); + while (zipEntry != null) { + String name = zipEntry.getName(); + entryNames.add(name); + zipEntry = zip.getNextZipEntry(); + } + } + + assertThat(entryNames.size()).isEqualTo(1); + assertThat(Iterables.getOnlyElement(entryNames)).isEqualTo( + String.format("%s/%s", abbreviated, PushOneCommit.FILE_NAME)); + } + + private String command(PushOneCommit.Result r, String abbreviated) { + String c = "-f=zip " + + "-9 " + + "--prefix=" + abbreviated + "/ " + + r.getCommit().name() + " " + + PushOneCommit.FILE_NAME; + return c; + } + + private void archiveNotPermitted() throws Exception { + PushOneCommit.Result r = createChange(); + String abbreviated = r.getCommitId().abbreviate(8).name(); + String c = command(r, abbreviated); + + InputStream out = + sshSession.exec2("git-upload-archive " + project.get(), + argumentsToInputStream(c)); + + // Wrap with PacketLineIn to read ACK bytes from output stream + PacketLineIn in = new PacketLineIn(out); + String tmp = in.readString(); + assertThat(tmp).isEqualTo("ACK"); + tmp = in.readString(); + tmp = in.readString(); + tmp = tmp.substring(1); + assertThat(tmp).isEqualTo("fatal: upload-archive not permitted"); + } + + private InputStream argumentsToInputStream(String c) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PacketLineOut pctOut = new PacketLineOut(out); + for (String arg : Splitter.on(' ').split(c)) { + pctOut.writeString("argument " + arg); + } + pctOut.end(); + return new ByteArrayInputStream(out.toByteArray()); + } +}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java index 5870f91..fcacf78 100644 --- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java +++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -37,7 +37,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -51,7 +53,7 @@ private final DefaultCacheFactory defaultFactory; private final Config config; - private final File cacheDir; + private final Path cacheDir; private final List<H2CacheImpl<?, ?>> caches; private final DynamicMap<Cache<?, ?>> cacheMap; private final ExecutorService executor; @@ -65,23 +67,7 @@ DynamicMap<Cache<?, ?>> cacheMap) { defaultFactory = defaultCacheFactory; config = cfg; - - File loc = site.resolve(cfg.getString("cache", null, "directory")); - if (loc == null) { - cacheDir = null; - } else if (loc.exists() || loc.mkdirs()) { - if (loc.canWrite()) { - log.info("Enabling disk cache " + loc.getAbsolutePath()); - cacheDir = loc; - } else { - log.warn("Can't write to disk cache: " + loc.getAbsolutePath()); - cacheDir = null; - } - } else { - log.warn("Can't create disk cache: " + loc.getAbsolutePath()); - cacheDir = null; - } - + cacheDir = getCacheDir(site, cfg.getString("cache", null, "directory")); caches = Lists.newLinkedList(); this.cacheMap = cacheMap; @@ -103,6 +89,27 @@ } } + private static Path getCacheDir(SitePaths site, String name) { + if (name == null) { + return null; + } + Path loc = site.resolve(name); + if (!Files.exists(loc)) { + try { + Files.createDirectories(loc); + } catch (IOException e) { + log.warn("Can't create disk cache: " + loc.toAbsolutePath()); + return null; + } + } + if (!Files.isWritable(loc)) { + log.warn("Can't write to disk cache: " + loc.toAbsolutePath()); + return null; + } + log.info("Enabling disk cache " + loc.toAbsolutePath()); + return loc; + } + @Override public void start() { if (executor != null) { @@ -213,8 +220,7 @@ TypeLiteral<K> keyType, long maxSize, Long expireAfterWrite) { - File db = new File(cacheDir, name).getAbsoluteFile(); - String url = "jdbc:h2:" + db.toURI().toString(); + String url = "jdbc:h2:" + cacheDir.resolve(name).toUri(); return new SqlStore<>(url, keyType, maxSize, expireAfterWrite == null ? 0 : expireAfterWrite.longValue()); }
diff --git a/gerrit-common/BUCK b/gerrit-common/BUCK index 88e503e..484136a 100644 --- a/gerrit-common/BUCK +++ b/gerrit-common/BUCK
@@ -51,6 +51,7 @@ '//lib:guava', '//lib/jgit:jgit', '//lib/joda:joda-time', + '//lib/log:api', ], visibility = ['PUBLIC'], )
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java index bed10d6..2335b8d1 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java
@@ -21,6 +21,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; public class FileUtil { @@ -42,6 +44,11 @@ } } + public static void chmod(final int mode, final Path path) { + // TODO(dborowitz): Is there a portable way to do this with NIO? + chmod(mode, path.toFile()); + } + public static void chmod(final int mode, final File path) { path.setReadable(false, false /* all */); path.setWritable(false, false /* all */); @@ -61,6 +68,33 @@ } } + /** + * Get the last modified time of a path. + * <p> + * Equivalent to {@code File#lastModified()}, returning 0 on errors, including + * file not found. Callers that prefer exceptions can use {@link + * Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)}. + * + * @param p path. + * @return last modified time, in milliseconds since epoch. + */ + public static long lastModified(Path p) { + try { + return Files.getLastModifiedTime(p).toMillis(); + } catch (IOException e) { + return 0; + } + } + + public static Path mkdirsOrDie(Path p, String errMsg) { + try { + Files.createDirectories(p); + return p; + } catch (IOException e) { + throw new Die(errMsg + ": " + p, e); + } + } + private FileUtil() { } -} \ No newline at end of file +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java index c45d9f9..9a30696 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
@@ -16,7 +16,6 @@ import com.google.common.collect.Sets; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,6 +24,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Path; import java.util.Arrays; import java.util.Set; @@ -53,7 +53,7 @@ }.start(); } - public static void loadJARs(File... jars) { + public static void loadJARs(Iterable<Path> jars) { ClassLoader cl = IoUtil.class.getClassLoader(); if (!(cl instanceof URLClassLoader)) { throw noAddURL("Not loaded by URLClassLoader", null); @@ -71,9 +71,9 @@ } Set<URL> have = Sets.newHashSet(Arrays.asList(urlClassLoader.getURLs())); - for (File path : jars) { + for (Path path : jars) { try { - URL url = path.toURI().toURL(); + URL url = path.toUri().toURL(); if (have.add(url)) { addURL.invoke(cl, url); } @@ -86,6 +86,10 @@ } } + public static void loadJARs(Path... jars) { + loadJARs(Arrays.asList(jars)); + } + private static UnsupportedOperationException noAddURL(String m, Throwable why) { String prefix = "Cannot extend classpath: "; return new UnsupportedOperationException(prefix + m, why);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java b/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java index ffdae9d..27dc639 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java
@@ -14,18 +14,18 @@ package com.google.gerrit.common; -import java.io.File; +import java.nio.file.Path; import java.util.Objects; public class PluginData { public final String name; public final String version; - public final File pluginFile; + public final Path pluginPath; - public PluginData(String name, String version, File pluginFile) { + public PluginData(String name, String version, Path pluginPath) { this.name = name; this.version = version; - this.pluginFile = pluginFile; + this.pluginPath = pluginPath; } @Override @@ -33,13 +33,13 @@ if (obj instanceof PluginData) { PluginData o = (PluginData) obj; return Objects.equals(name, o.name) && Objects.equals(version, o.version) - && Objects.equals(pluginFile, o.pluginFile); + && Objects.equals(pluginPath, o.pluginPath); } return super.equals(obj); } @Override public int hashCode() { - return Objects.hash(name, version, pluginFile); + return Objects.hash(name, version, pluginPath); } -} \ No newline at end of file +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java index a98e0a5..2511a51 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java
@@ -14,41 +14,57 @@ package com.google.gerrit.common; -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.Comparator; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.List; public final class SiteLibraryLoaderUtil { + private static final Logger log = + LoggerFactory.getLogger(SiteLibraryLoaderUtil.class); - public static void loadSiteLib(File libdir) { - File[] jars = listJars(libdir); - if (jars != null && 0 < jars.length) { - Arrays.sort(jars, new Comparator<File>() { - @Override - public int compare(File a, File b) { - // Sort by reverse last-modified time so newer JARs are first. - int cmp = Long.compare(b.lastModified(), a.lastModified()); - if (cmp != 0) { - return cmp; - } - return a.getName().compareTo(b.getName()); - } - }); - IoUtil.loadJARs(jars); + public static void loadSiteLib(Path libdir) { + try { + IoUtil.loadJARs(listJars(libdir)); + } catch (IOException e) { + log.error("Error scanning lib directory " + libdir, e); } } - public static File[] listJars(File libdir) { - File[] jars = libdir.listFiles(new FileFilter() { + public static List<Path> listJars(Path dir) throws IOException { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File path) { - String name = path.getName(); - return (name.endsWith(".jar") || name.endsWith(".zip")) - && path.isFile(); + public boolean accept(Path entry) throws IOException { + String name = entry.getFileName().toString(); + return (name.endsWith(".jar") || name.endsWith(".zip")) + && Files.isRegularFile(entry); } - }); - return jars; + }; + try (DirectoryStream<Path> jars = Files.newDirectoryStream(dir, filter)) { + return new Ordering<Path>() { + @Override + public int compare(Path a, Path b) { + // Sort by reverse last-modified time so newer JARs are first. + return ComparisonChain.start() + .compare(lastModified(b), lastModified(a)) + .compare(a, b) + .result(); + } + }.sortedCopy(jars); + } catch (NoSuchFileException nsfe) { + return ImmutableList.of(); + } } private SiteLibraryLoaderUtil() {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java index a744122..d0f8cd3 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java
@@ -107,7 +107,7 @@ return latest; } - public java.sql.Timestamp getLastUpdatedOn() { + public Timestamp getLastUpdatedOn() { return lastUpdatedOn; } }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java index db78c4d..1b98b09 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
@@ -136,7 +136,9 @@ parentMap.put(parentUuid, l); } l.add(c); - if (parentUuid == null) rootComments.add(c); + if (parentUuid == null) { + rootComments.add(c); + } } // Add the comments in the list, starting with the head and then going through all the
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java index d911390..045591c 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -16,10 +16,7 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account.FieldName; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; import com.google.gerrit.reviewdb.client.AuthType; -import com.google.gerrit.reviewdb.client.Project; import java.util.List; import java.util.Set; @@ -36,17 +33,11 @@ protected boolean httpPasswordSettingsEnabled = true; protected GitwebConfig gitweb; - protected boolean useContributorAgreements; - protected boolean useContactInfo; - protected boolean allowRegisterNewEmail; protected AuthType authType; - protected Set<DownloadScheme> downloadSchemes; - protected Set<DownloadCommand> downloadCommands; protected String gitDaemonUrl; protected String gitHttpUrl; protected String sshdAddress; protected String editFullNameUrl; - protected Project.NameKey wildProject; protected Set<Account.FieldName> editableAccountFields; protected boolean documentationAvailable; protected String anonymousCowardName; @@ -138,30 +129,10 @@ httpPasswordUrl = url; } - public AuthType getAuthType() { - return authType; - } - public void setAuthType(final AuthType t) { authType = t; } - public Set<DownloadScheme> getDownloadSchemes() { - return downloadSchemes; - } - - public void setDownloadSchemes(final Set<DownloadScheme> s) { - downloadSchemes = s; - } - - public Set<DownloadCommand> getDownloadCommands() { - return downloadCommands; - } - - public void setDownloadCommands(final Set<DownloadCommand> downloadCommands) { - this.downloadCommands = downloadCommands; - } - public GitwebConfig getGitwebLink() { return gitweb; } @@ -170,22 +141,6 @@ gitweb = w; } - public boolean isUseContributorAgreements() { - return useContributorAgreements; - } - - public void setUseContributorAgreements(final boolean r) { - useContributorAgreements = r; - } - - public boolean isUseContactInfo() { - return useContactInfo; - } - - public void setUseContactInfo(final boolean r) { - useContactInfo = r; - } - public String getGitDaemonUrl() { return gitDaemonUrl; } @@ -216,22 +171,6 @@ sshdAddress = addr; } - public Project.NameKey getWildProject() { - return wildProject; - } - - public void setWildProject(final Project.NameKey wp) { - wildProject = wp; - } - - public boolean canEdit(final Account.FieldName f) { - return editableAccountFields.contains(f); - } - - public Set<Account.FieldName> getEditableAccountFields() { - return editableAccountFields; - } - public void setEditableAccountFields(final Set<Account.FieldName> af) { editableAccountFields = af; } @@ -261,9 +200,9 @@ } public boolean siteHasUsernames() { - if (getAuthType() == AuthType.CUSTOM_EXTENSION + if (authType == AuthType.CUSTOM_EXTENSION && getHttpPasswordUrl() != null - && !canEdit(FieldName.USER_NAME)) { + && !editableAccountFields.contains(FieldName.USER_NAME)) { return false; } return true;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java index 8219d27..0aff539 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java
@@ -47,10 +47,6 @@ type = new GitWebType(); // The custom name is not defined, let's keep the old style of using GitWeb type.setLinkName("gitweb"); - - } else if (name.equalsIgnoreCase("disabled")) { - type = null; - } else { type = null; }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java index 430c23c..fa3d7a8 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -30,6 +30,7 @@ public Theme theme; public List<String> plugins; public List<Message> messages; + public Integer pluginsLoadTimeout; public boolean isNoteDbEnabled; public static class Theme {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java index 046df1d..7bab43a 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
@@ -57,6 +57,8 @@ protected boolean intralineFailure; protected boolean intralineTimeout; protected boolean binary; + protected transient String commitIdA; + protected transient String commitIdB; public PatchScript(final Change.Key ck, final ChangeType ct, final String on, final String nn, final FileMode om, final FileMode nm, @@ -65,7 +67,8 @@ final List<Edit> e, final DisplayMethod ma, final DisplayMethod mb, final String mta, final String mtb, final CommentDetail cd, final List<Patch> hist, final boolean hf, final boolean id, - final boolean idf, final boolean idt, boolean bin) { + final boolean idf, final boolean idt, boolean bin, + final String cma, final String cmb) { changeId = ck; changeType = ct; oldName = on; @@ -88,6 +91,8 @@ intralineFailure = idf; intralineTimeout = idt; binary = bin; + commitIdA = cma; + commitIdB = cmb; } protected PatchScript() { @@ -200,4 +205,12 @@ public boolean isBinary() { return binary; } + + public String getCommitIdA() { + return commitIdA; + } + + public String getCommitIdB() { + return commitIdB; + } }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java index 7be8e4e..8d09b88 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java
@@ -125,11 +125,15 @@ r.append(' '); } else { if (getMin() != getMax()) { - if (0 <= getMin()) r.append('+'); + if (0 <= getMin()) { + r.append('+'); + } r.append(getMin()); r.append(".."); } - if (0 <= getMax()) r.append('+'); + if (0 <= getMax()) { + r.append('+'); + } r.append(getMax()); r.append(' '); }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java index 3ba7adf..ec5ca06 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
@@ -126,8 +126,12 @@ @Override public int compareTo(PermissionRule o) { int cmp = action(this) - action(o); - if (cmp == 0) cmp = range(o) - range(this); - if (cmp == 0) cmp = group(this).compareTo(group(o)); + if (cmp == 0) { + cmp = range(o) - range(this); + } + if (cmp == 0) { + cmp = group(this).compareTo(group(o)); + } return cmp; }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/ProjectCreationFailedException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/ProjectCreationFailedException.java deleted file mode 100644 index 0437d77..0000000 --- a/gerrit-common/src/main/java/com/google/gerrit/common/errors/ProjectCreationFailedException.java +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright (C) 2011 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.common.errors; - -/** Error indicating failed to create new project. */ -public class ProjectCreationFailedException extends Exception { - private static final long serialVersionUID = 1L; - - public ProjectCreationFailedException(final String message) { - this(message, null); - } - - public ProjectCreationFailedException(final String message, - final Throwable why) { - super(message, why); - } -}
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml index 38b802a..d0204e4 100644 --- a/gerrit-extension-api/pom.xml +++ b/gerrit-extension-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-extension-api</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Extension API</name> <description>API for Gerrit Extensions</description>
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java index 75238a8..4893beff 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java
@@ -18,24 +18,25 @@ import com.google.inject.BindingAnnotation; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.Target; /** * Local path where a plugin can store its own private data. * <p> * A plugin or extension may receive this string by Guice injection to discover * a directory where it can store configuration or other data that is private: + * <p> + * This binding is on both {@link java.io.File} and {@link java.nio.file.Path}, + * pointing to the same location. The {@code File} version should be considered + * deprecated and may be removed in a future version. * * <pre> * {@literal @Inject} - * MyType(@PluginData java.io.File myDir) { - * new FileInputStream(new File(myDir, "my.config")); + * MyType(@PluginData java.nio.file.Path myDir) { + * this.in = Files.newInputStream(myDir.resolve("my.config")); * } * </pre> */ -@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RUNTIME) @BindingAnnotation public @interface PluginData {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java index cc5807b..1435d9e 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/GerritApi.java
@@ -16,12 +16,14 @@ import com.google.gerrit.extensions.api.accounts.Accounts; import com.google.gerrit.extensions.api.changes.Changes; +import com.google.gerrit.extensions.api.groups.Groups; import com.google.gerrit.extensions.api.projects.Projects; import com.google.gerrit.extensions.restapi.NotImplementedException; public interface GerritApi { public Accounts accounts(); public Changes changes(); + public Groups groups(); public Projects projects(); /** @@ -40,6 +42,11 @@ } @Override + public Groups groups() { + throw new NotImplementedException(); + } + + @Override public Projects projects() { throw new NotImplementedException(); }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java index 71a93d3..32f8488 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java
@@ -14,9 +14,12 @@ package com.google.gerrit.extensions.api.accounts; +import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.RestApiException; +import java.util.List; + public interface Accounts { /** * Look up an account by ID. @@ -42,6 +45,69 @@ AccountApi self() throws RestApiException; /** + * Suggest users for a given query. + * <p> + * Example code: + * {@code suggestAccounts().withQuery("Reviewer").withLimit(5).get()} + * + * @return API for setting parameters and getting result. + */ + SuggestAccountsRequest suggestAccounts() throws RestApiException; + + /** + * Suggest users for a given query. + * <p> + * Shortcut API for {@code suggestAccounts().withQuery(String)}. + * + * @see #suggestAccounts() + */ + SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException; + + /** + * API for setting parameters and getting result. + * Used for {@code suggestAccounts()}. + * + * @see #suggestAccounts() + */ + public abstract class SuggestAccountsRequest { + private String query; + private int limit; + + /** + * Executes query and returns a list of accounts. + */ + public abstract List<AccountInfo> get() throws RestApiException; + + /** + * Set query. + * + * @param query needs to be in human-readable form. + */ + public SuggestAccountsRequest withQuery(String query) { + this.query = query; + return this; + } + + /** + * Set limit for returned list of accounts. + * Optional; server-default is used when not provided. + */ + public SuggestAccountsRequest withLimit(int limit) { + this.limit = limit; + return this; + } + + public String getQuery() { + return query; + } + + public int getLimit() { + return limit; + } + } + + /** * A default implementation which allows source compatibility * when adding new methods to the interface. **/ @@ -55,5 +121,16 @@ public AccountApi self() throws RestApiException { throw new NotImplementedException(); } + + @Override + public SuggestAccountsRequest suggestAccounts() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java index 06f0a75..6781af2 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -16,6 +16,7 @@ import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.SuggestedReviewerInfo; import com.google.gerrit.extensions.restapi.NotImplementedException; @@ -23,6 +24,7 @@ import java.util.EnumSet; import java.util.List; +import java.util.Map; import java.util.Set; public interface ChangeApi { @@ -106,6 +108,24 @@ */ Set<String> getHashtags() throws RestApiException; + /** + * Get all published comments on a change. + * + * @return comments in a map keyed by path; comments have the {@code revision} + * field set to indicate their patch set. + * @throws RestApiException + */ + Map<String, List<CommentInfo>> comments() throws RestApiException; + + /** + * Get all draft comments for the current user on a change. + * + * @return drafts in a map keyed by path; comments have the {@code revision} + * field set to indicate their patch set. + * @throws RestApiException + */ + Map<String, List<CommentInfo>> drafts() throws RestApiException; + ChangeInfo check() throws RestApiException; ChangeInfo check(FixInput fix) throws RestApiException; @@ -250,6 +270,16 @@ } @Override + public Map<String, List<CommentInfo>> comments() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public Map<String, List<CommentInfo>> drafts() throws RestApiException { + throw new NotImplementedException(); + } + + @Override public ChangeInfo check() throws RestApiException { throw new NotImplementedException(); }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java index 8ab3080..da8aeb2 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/Changes.java
@@ -116,6 +116,23 @@ public EnumSet<ListChangesOption> getOptions() { return options; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()) + .append('{') + .append(query); + if (limit != 0) { + sb.append(", limit=").append(limit); + } + if (start != 0) { + sb.append(", start=").append(start); + } + if (!options.isEmpty()) { + sb.append("options=").append(options); + } + return sb.append('}').toString(); + } } /**
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java index dd2ce92..2d2e4e9 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
@@ -61,7 +61,17 @@ public String onBehalfOf; public static enum DraftHandling { - DELETE, PUBLISH, KEEP + /** Delete pending drafts on this revision only. */ + DELETE, + + /** Publish pending drafts on this revision only. */ + PUBLISH, + + /** Leave pending drafts alone. */ + KEEP, + + /** Publish pending drafts on all revisions. */ + PUBLISH_ALL_REVISIONS } public static enum NotifyHandling {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java index b940cc9..3d1e3bd 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
@@ -14,6 +14,7 @@ package com.google.gerrit.extensions.api.changes; +import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.FileInfo; import com.google.gerrit.extensions.common.MergeableInfo; @@ -49,11 +50,16 @@ Map<String, List<CommentInfo>> comments() throws RestApiException; Map<String, List<CommentInfo>> drafts() throws RestApiException; + List<CommentInfo> commentsAsList() throws RestApiException; + List<CommentInfo> draftsAsList() throws RestApiException; + DraftApi createDraft(DraftInput in) throws RestApiException; DraftApi draft(String id) throws RestApiException; CommentApi comment(String id) throws RestApiException; + Map<String, ActionInfo> actions() throws RestApiException; + /** * A default implementation which allows source compatibility * when adding new methods to the interface. @@ -145,6 +151,16 @@ } @Override + public List<CommentInfo> commentsAsList() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public List<CommentInfo> draftsAsList() throws RestApiException { + throw new NotImplementedException(); + } + + @Override public Map<String, List<CommentInfo>> drafts() throws RestApiException { throw new NotImplementedException(); } @@ -163,5 +179,10 @@ public CommentApi comment(String id) throws RestApiException { throw new NotImplementedException(); } + + @Override + public Map<String, ActionInfo> actions() throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java new file mode 100644 index 0000000..417e371 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupApi.java
@@ -0,0 +1,135 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.groups; + +import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.common.GroupOptionsInfo; +import com.google.gerrit.extensions.restapi.RestApiException; + +import java.util.List; + +public interface GroupApi { + /** @return group info with no {@code ListGroupsOption}s set. */ + GroupInfo get() throws RestApiException; + + /** @return group info with all {@code ListGroupsOption}s set. */ + GroupInfo detail() throws RestApiException; + + /** @return group name. */ + String name() throws RestApiException; + + /** + * Set group name. + * + * @param name new name. + * @throws RestApiException + */ + void name(String name) throws RestApiException; + + /** @return owning group info. */ + GroupInfo owner() throws RestApiException; + + /** + * Set group owner. + * + * @param owner identifier of new group owner. + * @throws RestApiException + */ + void owner(String owner) throws RestApiException; + + /** @return group description. */ + String description() throws RestApiException; + + /** + * Set group decsription. + * + * @param description new description. + * @throws RestApiException + */ + void description(String description) throws RestApiException; + + /** @return group options. */ + GroupOptionsInfo options() throws RestApiException; + + /** + * Set group options. + * + * @param options new options. + * @throws RestApiException + */ + void options(GroupOptionsInfo options) throws RestApiException; + + /** + * List group members, non-recursively. + * + * @return group members. + * @throws RestApiException + */ + List<AccountInfo> members() throws RestApiException; + + /** + * List group members. + * + * @param recursive whether to recursively included groups. + * @return group members. + * @throws RestApiException + */ + List<AccountInfo> members(boolean recursive) throws RestApiException; + + /** + * Add members to a group. + * + * @param members list of member identifiers, in any format accepted by + * {@link com.google.gerrit.extensions.api.accounts.Accounts#id(String)} + * @throws RestApiException + */ + void addMembers(String... members) throws RestApiException; + + /** + * Remove members from a group. + * + * @param members list of member identifiers, in any format accepted by + * {@link com.google.gerrit.extensions.api.accounts.Accounts#id(String)} + * @throws RestApiException + */ + void removeMembers(String... members) throws RestApiException; + + /** + * List included groups. + * + * @return included groups. + * @throws RestApiException + */ + List<GroupInfo> includedGroups() throws RestApiException; + + /** + * Add groups to be included in this one. + * + * @param groups list of group identifiers, in any format accepted by + * {@link Groups#id(String)} + * @throws RestApiException + */ + void addGroups(String... groups) throws RestApiException; + + /** + * Remove included groups from this one. + * + * @param groups list of group identifiers, in any format accepted by + * {@link Groups#id(String)} + * @throws RestApiException + */ + void removeGroups(String... groups) throws RestApiException; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupInput.java new file mode 100644 index 0000000..28665fe --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/GroupInput.java
@@ -0,0 +1,22 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.groups; + +public class GroupInput { + public String name; + public String description; + public Boolean visibleToAll; + public String ownerId; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java new file mode 100644 index 0000000..ab09e5f --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
@@ -0,0 +1,167 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.groups; + +import com.google.gerrit.extensions.client.ListGroupsOption; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.RestApiException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +public interface Groups { + /** + * Look up a group by ID. + * <p> + * <strong>Note:</strong> This method eagerly reads the group. Methods that + * mutate the group do not necessarily re-read the group. Therefore, calling a + * getter method on an instance after calling a mutation method on that same + * instance is not guaranteed to reflect the mutation. It is not recommended + * to store references to {@code groupApi} instances. + * + * @param id any identifier supported by the REST API, including group name or + * UUID. + * @return API for accessing the group. + * @throws RestApiException if an error occurred. + */ + GroupApi id(String id) throws RestApiException; + + /** Create a new group with the given name and default options. */ + GroupApi create(String name) throws RestApiException; + + /** Create a new group. */ + GroupApi create(GroupInput input) throws RestApiException; + + /** @return new request for listing groups. */ + ListRequest list(); + + public abstract class ListRequest { + private final EnumSet<ListGroupsOption> options = + EnumSet.noneOf(ListGroupsOption.class); + private final List<String> projects = new ArrayList<>(); + private final List<String> groups = new ArrayList<>(); + + private boolean visibleToAll; + private String user; + private boolean owned; + private int limit; + private int start; + private String substring; + + public List<GroupInfo> get() throws RestApiException { + Map<String, GroupInfo> map = getAsMap(); + List<GroupInfo> result = new ArrayList<>(map.size()); + for (Map.Entry<String, GroupInfo> e : map.entrySet()) { + // ListGroups "helpfully" nulls out names when converting to a map. + e.getValue().name = e.getKey(); + result.add(e.getValue()); + } + return Collections.unmodifiableList(result); + } + + public abstract Map<String, GroupInfo> getAsMap() throws RestApiException; + + public ListRequest addOption(ListGroupsOption option) { + options.add(option); + return this; + } + + public ListRequest addOptions(ListGroupsOption... options) { + return addOptions(Arrays.asList(options)); + } + + public ListRequest addOptions(Iterable<ListGroupsOption> options) { + for (ListGroupsOption option : options) { + this.options.add(option); + } + return this; + } + + public ListRequest withProject(String project) { + projects.add(project); + return this; + } + + public ListRequest addGroup(String uuid) { + groups.add(uuid); + return this; + } + + public ListRequest withVisibleToAll(boolean visible) { + visibleToAll = visible; + return this; + } + + public ListRequest withUser(String user) { + this.user = user; + return this; + } + + public ListRequest withLimit(int limit) { + this.limit = limit; + return this; + } + + public ListRequest withStart(int start) { + this.start = start; + return this; + } + + public ListRequest withSubstring(String substring) { + this.substring = substring; + return this; + } + + public EnumSet<ListGroupsOption> getOptions() { + return options; + } + + public List<String> getProjects() { + return Collections.unmodifiableList(projects); + } + + public List<String> getGroups() { + return Collections.unmodifiableList(groups); + } + + public boolean getVisibleToAll() { + return visibleToAll; + } + + public String getUser() { + return user; + } + + public boolean getOwned() { + return owned; + } + + public int getLimit() { + return limit; + } + + public int getStart() { + return start; + } + + public String getSubstring() { + return substring; + } + } +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchApi.java index f88a2cb..91cb70e 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchApi.java
@@ -20,6 +20,10 @@ public interface BranchApi { BranchApi create(BranchInput in) throws RestApiException; + BranchInfo get() throws RestApiException; + + void delete() throws RestApiException; + /** * A default implementation which allows source compatibility * when adding new methods to the interface. @@ -29,5 +33,15 @@ public BranchApi create(BranchInput in) throws RestApiException { throw new NotImplementedException(); } + + @Override + public BranchInfo get() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public void delete() throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java new file mode 100644 index 0000000..b973806 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java
@@ -0,0 +1,29 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.projects; + +import com.google.gerrit.extensions.common.ActionInfo; +import com.google.gerrit.extensions.common.WebLinkInfo; + +import java.util.List; +import java.util.Map; + +public class BranchInfo { + public String ref; + public String revision; + public Boolean canDelete; + public Map<String, ActionInfo> actions; + public List<WebLinkInfo> webLinks; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ChildProjectApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ChildProjectApi.java new file mode 100644 index 0000000..a930f0d --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ChildProjectApi.java
@@ -0,0 +1,40 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.projects; + +import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.NotImplementedException; +import com.google.gerrit.extensions.restapi.RestApiException; + +public interface ChildProjectApi { + ProjectInfo get() throws RestApiException; + ProjectInfo get(boolean recursive) throws RestApiException; + + /** + * A default implementation which allows source compatibility + * when adding new methods to the interface. + **/ + public class NotImplemented implements ChildProjectApi { + @Override + public ProjectInfo get() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public ProjectInfo get(boolean recursive) throws RestApiException { + throw new NotImplementedException(); + } + } +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java index 07a48a1..102b1ce 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -18,10 +18,67 @@ import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.RestApiException; +import java.util.List; + public interface ProjectApi { ProjectApi create() throws RestApiException; ProjectApi create(ProjectInput in) throws RestApiException; - ProjectInfo get(); + ProjectInfo get() throws RestApiException; + + String description() throws RestApiException; + void description(PutDescriptionInput in) throws RestApiException; + + ListBranchesRequest branches(); + + public abstract class ListBranchesRequest { + private int limit; + private int start; + private String substring; + private String regex; + + public abstract List<BranchInfo> get() throws RestApiException; + + public ListBranchesRequest withLimit(int limit) { + this.limit = limit; + return this; + } + + public ListBranchesRequest withStart(int start) { + this.start = start; + return this; + } + + public ListBranchesRequest withSubstring(String substring) { + this.substring = substring; + return this; + } + + public ListBranchesRequest withRegex(String regex) { + this.regex = regex; + return this; + } + + public int getLimit() { + return limit; + } + + public int getStart() { + return start; + } + + public String getSubstring() { + return substring; + } + + public String getRegex() { + return regex; + } + + } + + List<ProjectInfo> children() throws RestApiException; + List<ProjectInfo> children(boolean recursive) throws RestApiException; + ChildProjectApi child(String name) throws RestApiException; /** * Look up a branch by refname. @@ -33,9 +90,10 @@ * to store references to {@code BranchApi} instances. * * @param ref branch name, with or without "refs/heads/" prefix. + * @throws RestApiException if a problem occurred reading the project. * @return API for accessing the branch. */ - BranchApi branch(String ref); + BranchApi branch(String ref) throws RestApiException; /** * A default implementation which allows source compatibility @@ -53,12 +111,43 @@ } @Override - public ProjectInfo get() { + public ProjectInfo get() throws RestApiException { throw new NotImplementedException(); } @Override - public BranchApi branch(String ref) { + public String description() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public void description(PutDescriptionInput in) + throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public ListBranchesRequest branches() { + throw new NotImplementedException(); + } + + @Override + public List<ProjectInfo> children() { + throw new NotImplementedException(); + } + + @Override + public List<ProjectInfo> children(boolean recursive) throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public ChildProjectApi child(String name) throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public BranchApi branch(String ref) throws RestApiException { throw new NotImplementedException(); } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java index 736d375..0e848b9 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
@@ -18,7 +18,11 @@ import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.RestApiException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.SortedMap; public interface Projects { /** @@ -36,15 +40,54 @@ */ ProjectApi name(String name) throws RestApiException; + /** + * Create a project using the default configuration. + * + * @param name project name. + * @return API for accessing the newly-created project. + * @throws RestApiException if an error occurred. + */ + ProjectApi create(String name) throws RestApiException; + + /** + * Create a project. + * + * @param in project creation input; name must be set. + * @return API for accessing the newly-created project. + * @throws RestApiException if an error occurred. + */ + ProjectApi create(ProjectInput in) throws RestApiException; + ListRequest list(); public abstract class ListRequest { + public static enum FilterType { + CODE, PARENT_CANDIDATES, PERMISSIONS, ALL + } + + private final List<String> branches = new ArrayList<>(); private boolean description; private String prefix; + private String substring; + private String regex; private int limit; private int start; + private boolean showTree; + private FilterType type = FilterType.ALL; - public abstract List<ProjectInfo> get() throws RestApiException; + public List<ProjectInfo> get() throws RestApiException { + Map<String, ProjectInfo> map = getAsMap(); + List<ProjectInfo> result = new ArrayList<>(map.size()); + for (Map.Entry<String, ProjectInfo> e : map.entrySet()) { + // ListProjects "helpfully" nulls out names when converting to a map. + e.getValue().name = e.getKey(); + result.add(e.getValue()); + } + return Collections.unmodifiableList(result); + } + + public abstract SortedMap<String, ProjectInfo> getAsMap() + throws RestApiException; public ListRequest withDescription(boolean description) { this.description = description; @@ -56,6 +99,16 @@ return this; } + public ListRequest withSubstring(String substring) { + this.substring = substring; + return this; + } + + public ListRequest withRegex(String regex) { + this.regex = regex; + return this; + } + public ListRequest withLimit(int limit) { this.limit = limit; return this; @@ -66,6 +119,21 @@ return this; } + public ListRequest addShowBranch(String branch) { + branches.add(branch); + return this; + } + + public ListRequest withTree(boolean show) { + showTree = show; + return this; + } + + public ListRequest withType(FilterType type) { + this.type = type != null ? type : FilterType.ALL; + return this; + } + public boolean getDescription() { return description; } @@ -74,6 +142,14 @@ return prefix; } + public String getSubstring() { + return substring; + } + + public String getRegex() { + return regex; + } + public int getLimit() { return limit; } @@ -81,6 +157,18 @@ public int getStart() { return start; } + + public List<String> getBranches() { + return Collections.unmodifiableList(branches); + } + + public boolean getShowTree() { + return showTree; + } + + public FilterType getFilterType() { + return type; + } } /** @@ -94,6 +182,16 @@ } @Override + public ProjectApi create(ProjectInput in) throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public ProjectApi create(String name) throws RestApiException { + throw new NotImplementedException(); + } + + @Override public ListRequest list() { throw new NotImplementedException(); }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/PutDescriptionInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/PutDescriptionInput.java new file mode 100644 index 0000000..7ea9fb6 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/PutDescriptionInput.java
@@ -0,0 +1,23 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.api.projects; + +import com.google.gerrit.extensions.restapi.DefaultInput; + +public class PutDescriptionInput { + @DefaultInput + public String description; + public String commitMessage; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Comment.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Comment.java index e79df1c..b9863d7 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Comment.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Comment.java
@@ -17,6 +17,13 @@ import java.sql.Timestamp; public abstract class Comment { + /** + * Patch set number containing this commit. + * <p> + * Only set in contexts where comments may come from multiple patch sets. + */ + public Integer patchSet; + public String id; public String path; public Side side;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java index 54617a7..5caa903 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -15,6 +15,7 @@ package com.google.gerrit.extensions.client; import java.util.EnumSet; +import java.util.Set; /** Output options available for retrieval change details. */ public enum ListChangesOption { @@ -58,7 +59,10 @@ CHECK(15), /** Include allowed change actions client could perform. */ - CHANGE_ACTIONS(16); + CHANGE_ACTIONS(16), + + /** Include a copy of commit messages including review footers. */ + COMMIT_FOOTERS(17); private final int value; @@ -87,7 +91,7 @@ return r; } - public static int toBits(EnumSet<ListChangesOption> set) { + public static int toBits(Set<ListChangesOption> set) { int r = 0; for (ListChangesOption o : set) { r |= 1 << o.value;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListGroupsOption.java similarity index 96% rename from gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListGroupsOption.java index 9da7fd5..d87f73e 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/groups/ListGroupsOption.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListGroupsOption.java
@@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.common.groups; +package com.google.gerrit.extensions.client; import java.util.EnumSet; - /** Output options available when using {@code /groups/} RPCs. */ public enum ListGroupsOption { /** Return information on the direct group members. */
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java index d55580c..d26ea23 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java
@@ -32,5 +32,5 @@ COPIED, /** Sufficient amount of content changed to claim the file was rewritten. */ - REWRITE; + REWRITE }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DiffInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DiffInfo.java index 62b1dc7..58b2d39 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DiffInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/DiffInfo.java
@@ -42,6 +42,8 @@ } public static class FileMeta { + // The ID of the commit containing the file + public transient String commitId; // The name of the file public String name; // The content type of the file
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupInfo.java new file mode 100644 index 0000000..f956a03 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupInfo.java
@@ -0,0 +1,32 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.common; + +import java.util.List; + +public class GroupInfo extends GroupBaseInfo { + public String url; + public GroupOptionsInfo options; + + // These fields are only supplied for internal groups. + public String description; + public Integer groupId; + public String owner; + public String ownerId; + + // These fields are only supplied for internal groups, and only if requested. + public List<AccountInfo> members; + public List<GroupInfo> includes; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupOptionsInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupOptionsInfo.java new file mode 100644 index 0000000..074e1a4 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/GroupOptionsInfo.java
@@ -0,0 +1,19 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.common; + +public class GroupOptionsInfo { + public Boolean visibleToAll; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java index a117d07..d04b346 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java
@@ -16,7 +16,7 @@ public class ProblemInfo { public static enum Status { - FIXED, FIX_FAILED; + FIXED, FIX_FAILED } public String message;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java index 4b8eec1..7c71ba3 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/RevisionInfo.java
@@ -29,4 +29,5 @@ public CommitInfo commit; public Map<String, FileInfo> files; public Map<String, ActionInfo> actions; + public String commitWithFooters; }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java new file mode 100644 index 0000000..eb223c6 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java
@@ -0,0 +1,40 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.extensions.events; + +import com.google.gerrit.extensions.annotations.ExtensionPoint; + +import java.util.Properties; + +/** + * Notified whenever the garbage collector has run successfully on a project. + */ +@ExtensionPoint +public interface GarbageCollectorListener { + public interface Event { + /** @return The name of the project that has been garbage collected. */ + String getProjectName(); + + /** + * Properties describing the result of the garbage collection performed by + * JGit + * + * @see <a href="http://download.eclipse.org/jgit/site/3.7.0.201502260915-r/apidocs/org/eclipse/jgit/api/GarbageCollectCommand.html#call%28%29">GarbageCollectCommand</a> + */ + Properties getStatistics(); + } + + void onGarbageCollected(Event event); +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java index 49a697e..a838baf 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
@@ -25,6 +25,9 @@ String getRefName(); String getOldObjectId(); String getNewObjectId(); + boolean isCreate(); + boolean isDelete(); + boolean isNonFastForward(); } void onGitReferenceUpdated(Event event);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicItem.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicItem.java index 1388637..7de740dc 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicItem.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicItem.java
@@ -67,7 +67,9 @@ * <p> * Items must be defined in a Guice module before they can be bound: * <pre> + * {@code * DynamicSet.itemOf(binder(), new TypeLiteral<Thing<Foo>>() {}); + * } * </pre> * * @param binder a new binder created in the module.
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java index abf944a..b777899 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicMap.java
@@ -67,10 +67,12 @@ * Maps must be defined in a Guice module before they can be bound: * * <pre> + * {@code * DynamicMap.mapOf(binder(), new TypeLiteral<Thing<Bar>>(){}); * bind(new TypeLiteral<Thing<Bar>>() {}) * .annotatedWith(Exports.named("foo")) * .to(Impl.class); + * } * </pre> * * @param binder a new binder created in the module.
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java index 82613c7..8bc39a5 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -61,7 +61,9 @@ * <p> * Sets must be defined in a Guice module before they can be bound: * <pre> + * {@code * DynamicSet.setOf(binder(), new TypeLiteral<Thing<Foo>>() {}); + * } * </pre> * * @param binder a new binder created in the module.
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java index 18f356b..92fefed 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java
@@ -196,8 +196,9 @@ } catch (UnsupportedCharsetException | CharacterCodingException e) { // Fallback to ISO-8850-1 style encoding. StringBuilder r = new StringBuilder(data.length); - for (byte b : data) - r.append((char) (b & 0xff)); + for (byte b : data) { + r.append((char) (b & 0xff)); + } return r.toString(); } }
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java index b015274..191ffd7 100644 --- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java +++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
@@ -114,7 +114,7 @@ } private void populate(final Grid lists) { - int end[] = new int[5]; + int[] end = new int[5]; int column = 0; for (final KeyCommandSet set : combinedSetsByName()) { int row = end[column];
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/UserAgentRule.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/UserAgentRule.java index eb87e7f..6c820a8 100644 --- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/UserAgentRule.java +++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/UserAgentRule.java
@@ -67,9 +67,6 @@ if (v >= 8000) { return "ie8"; } - if (v >= 6000) { - return "ie6"; - } } return null;
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java index 69da38d..0e7f7eb 100644 --- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java +++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java
@@ -162,7 +162,7 @@ } /** - * Open an element, appending "<tagName>" to the buffer. + * Open an element, appending "{@code <tagName>}" to the buffer. * <p> * After the element is open the attributes may be manipulated until the next * {@code append}, {@code openElement}, {@code closeSelf} or
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java index c4e2167..0db7ea4 100644 --- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java +++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java
@@ -18,90 +18,90 @@ import com.google.gwt.resources.client.ImageResource; public interface Resources extends ClientBundle { + @Source("addFileComment.png") + public ImageResource addFileComment(); + + @Source("arrowDown.png") + public ImageResource arrowDown(); + @Source("arrowRight.png") public ImageResource arrowRight(); @Source("arrowUp.png") public ImageResource arrowUp(); - @Source("arrowDown.png") - public ImageResource arrowDown(); - - @Source("editText.png") - public ImageResource edit(); - - @Source("mediaFloppy.png") - public ImageResource save(); - - @Source("starOpen.png") - public ImageResource starOpen(); - - @Source("starFilled.png") - public ImageResource starFilled(); - - @Source("greenCheck.png") - public ImageResource greenCheck(); - - @Source("redNot.png") - public ImageResource redNot(); - - @Source("editUndo.png") - public ImageResource editUndo(); - - @Source("downloadIcon.png") - public ImageResource downloadIcon(); - - @Source("queryIcon.png") - public ImageResource queryIcon(); - - @Source("addFileComment.png") - public ImageResource addFileComment(); - - @Source("diffy26.png") - public ImageResource gerritAvatar26(); - - @Source("draftComments.png") - public ImageResource draftComments(); - - @Source("readOnly.png") - public ImageResource readOnly(); - - @Source("gear.png") - public ImageResource gear(); - - @Source("info.png") - public ImageResource info(); - - @Source("warning.png") - public ImageResource warning(); - - @Source("listAdd.png") - public ImageResource listAdd(); - - @Source("merge.png") - public ImageResource merge(); + @Source("deleteHover.png") + public ImageResource deleteHover(); @Source("deleteNormal.png") public ImageResource deleteNormal(); - @Source("deleteHover.png") - public ImageResource deleteHover(); + @Source("diffy26.png") + public ImageResource gerritAvatar26(); - @Source("undoNormal.png") - public ImageResource undoNormal(); + @Source("downloadIcon.png") + public ImageResource downloadIcon(); - @Source("goPrev.png") - public ImageResource goPrev(); + @Source("draftComments.png") + public ImageResource draftComments(); + + @Source("editText.png") + public ImageResource edit(); + + @Source("editUndo.png") + public ImageResource editUndo(); + + @Source("gear.png") + public ImageResource gear(); @Source("goNext.png") public ImageResource goNext(); + @Source("goPrev.png") + public ImageResource goPrev(); + @Source("goUp.png") public ImageResource goUp(); + @Source("greenCheck.png") + public ImageResource greenCheck(); + + @Source("info.png") + public ImageResource info(); + + @Source("listAdd.png") + public ImageResource listAdd(); + + @Source("mediaFloppy.png") + public ImageResource save(); + + @Source("merge.png") + public ImageResource merge(); + + @Source("queryIcon.png") + public ImageResource queryIcon(); + + @Source("readOnly.png") + public ImageResource readOnly(); + + @Source("redNot.png") + public ImageResource redNot(); + @Source("sideBySideDiff.png") public ImageResource sideBySideDiff(); + @Source("starFilled.png") + public ImageResource starFilled(); + + @Source("starOpen.png") + public ImageResource starOpen(); + + @Source("undoNormal.png") + public ImageResource undoNormal(); + @Source("unifiedDiff.png") public ImageResource unifiedDiff(); + + @Source("warning.png") + public ImageResource warning(); }
diff --git a/gerrit-gwtui/BUCK b/gerrit-gwtui/BUCK index aad5e0b..9eb0bf6 100644 --- a/gerrit-gwtui/BUCK +++ b/gerrit-gwtui/BUCK
@@ -2,6 +2,7 @@ include_defs('//tools/gwt-constants.defs') from multiprocessing import cpu_count +CPU_COUNT = cpu_count() DEPS = GWT_COMMON_DEPS + [ '//gerrit-gwtexpui:CSS', '//lib:gwtjsonrpc', @@ -29,8 +30,8 @@ name = 'ui_opt', modules = [MODULE], module_deps = [':ui_module'], - deps = DEPS + [':ui_dbg'], - local_workers = cpu_count(), + deps = DEPS + ([':ui_dbg'] if CPU_COUNT < 8 else []), + local_workers = CPU_COUNT, strict = True, experimental_args = GWT_COMPILER_ARGS, vm_args = GWT_JVM_ARGS, @@ -41,7 +42,7 @@ modules = [MODULE], module_deps = [':ui_module'], deps = DEPS + [':ui_dbg'], - local_workers = cpu_count(), + local_workers = CPU_COUNT, strict = True, experimental_args = GWT_COMPILER_ARGS + ['-compileReport'], vm_args = GWT_JVM_ARGS, @@ -54,7 +55,7 @@ optimize = 0, module_deps = [':ui_module'], deps = DEPS, - local_workers = cpu_count(), + local_workers = CPU_COUNT, strict = True, experimental_args = GWT_COMPILER_ARGS, vm_args = GWT_JVM_ARGS,
diff --git a/gerrit-gwtui/gwt.defs b/gerrit-gwtui/gwt.defs index cd206c0..0e5928d 100644 --- a/gerrit-gwtui/gwt.defs +++ b/gerrit-gwtui/gwt.defs
@@ -17,7 +17,7 @@ 'firefox', 'gecko1_8', 'safari', - 'msie', 'ie6', 'ie8', 'ie9', + 'msie', 'ie8', 'ie9', ] ALIASES = { 'chrome': 'safari',
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml index fd717ee..0f5065f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
@@ -40,5 +40,8 @@ <set-property name='gwt.logging.logLevel' value='SEVERE'/> + <!-- Disable GSS --> + <set-configuration-property name='CssResource.enableGss' value='false'/> + <entry-point class='com.google.gerrit.client.Gerrit'/> </module>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml index e6f3acb..c02518b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml
@@ -21,10 +21,9 @@ <when-property-is name="user.agent" value="gecko1_8" /> </replace-with> - <replace-with class="com.google.gerrit.client.ui.FancyFlexTableImplIE6"> + <replace-with class="com.google.gerrit.client.ui.FancyFlexTableImplIE8"> <when-type-is class="com.google.gerrit.client.ui.FancyFlexTableImpl" /> <any> - <when-property-is name="user.agent" value="ie6"/> <when-property-is name="user.agent" value="ie8"/> </any> </replace-with> @@ -32,7 +31,6 @@ <replace-with class="com.google.gerrit.client.Themer.ThemerIE"> <when-type-is class="com.google.gerrit.client.Themer" /> <any> - <when-property-is name="user.agent" value="ie6"/> <when-property-is name="user.agent" value="ie8"/> <when-property-is name="user.agent" value="ie9"/> <when-property-is name="user.agent" value="ie10"/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java index 053999a..7f17f4f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
@@ -72,7 +72,7 @@ } else if (isGerritServer(account)) { setVisible(true); setResource(Gerrit.RESOURCES.gerritAvatar26()); - } else if (account.has_avatar_info()) { + } else if (account.hasAvatarInfo()) { setVisible(false); AvatarInfo info = account.avatar(size); if (info != null) { @@ -121,7 +121,7 @@ } private static boolean isGerritServer(AccountInfo account) { - return account._account_id() == 0 + return account._accountId() == 0 && Util.C.messageNoAuthor().equals(account.name()); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java index e2bf142..e43139c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -341,7 +341,7 @@ private static String legacyChange(final String token) { final String s = skip(token); - final String t[] = s.split(",", 2); + final String[] t = s.split(",", 2); if (t.length > 1 && matchPrefix("patchset=", t[1])) { return PageLinks.toChange(PatchSet.Id.parse(t[0] + "," + skip(t[1]))); } @@ -695,7 +695,7 @@ } if (matchExact(SETTINGS_AGREEMENTS, token) - && Gerrit.getConfig().isUseContributorAgreements()) { + && Gerrit.info().auth().useContributorAgreements()) { return new MyAgreementsScreen(); } @@ -707,11 +707,13 @@ return new RegisterScreen("/" + skip(token)); } - if (matchPrefix("/VE/", token) || matchPrefix("VE,", token)) + if (matchPrefix("/VE/", token) || matchPrefix("VE,", token)) { return new ValidateEmailScreen(skip(token)); + } - if (matchExact(SETTINGS_NEW_AGREEMENT, token)) + if (matchExact(SETTINGS_NEW_AGREEMENT, token)) { return new NewAgreementScreen(); + } if (matchPrefix(SETTINGS_NEW_AGREEMENT + "/", token)) { return new NewAgreementScreen(skip(token));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java index 5b51b48..30980e1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -144,8 +144,8 @@ StringBuilder b = new StringBuilder().append(name); if (info.email() != null) { b.append(" <").append(info.email()).append(">"); - } else if (info._account_id() > 0) { - b.append(" (").append(info._account_id()).append(")"); + } else if (info._accountId() > 0) { + b.append(" (").append(info._accountId()).append(")"); } return b.toString(); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java index 296f93f..c176a09 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -28,6 +28,7 @@ import com.google.gerrit.client.changes.ChangeConstants; import com.google.gerrit.client.changes.ChangeListScreen; import com.google.gerrit.client.config.ConfigServerApi; +import com.google.gerrit.client.config.ServerInfo; import com.google.gerrit.client.extensions.TopMenu; import com.google.gerrit.client.extensions.TopMenuItem; import com.google.gerrit.client.extensions.TopMenuList; @@ -49,7 +50,6 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountDiffPreference; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; -import com.google.gerrit.reviewdb.client.AuthType; import com.google.gerrit.reviewdb.client.Project; import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.EntryPoint; @@ -101,11 +101,12 @@ GWT.create(GerritResources.class); public static final SystemInfoService SYSTEM_SVC; public static final EventBus EVENT_BUS = GWT.create(SimpleEventBus.class); - public static Themer THEMER = GWT.create(Themer.class); + public static final Themer THEMER = GWT.create(Themer.class); public static final String PROJECT_NAME_MENU_VAR = "${projectName}"; private static String myHost; private static GerritConfig myConfig; + private static ServerInfo myServerInfo; private static HostPageData.Theme myTheme; private static Account myAccount; private static String defaultScreenToken; @@ -288,6 +289,11 @@ return myConfig; } + /** Get the public configuration data used by this Gerrit instance. */ + public static ServerInfo info() { + return myServerInfo; + } + public static GitwebLink getGitwebLink() { GitwebConfig gw = getConfig().getGitwebLink(); return gw != null && gw.type != null ? new GitwebLink(gw) : null; @@ -426,8 +432,16 @@ initHostname(); Window.setTitle(M.windowTitle1(myHost)); - final HostPageDataService hpd = GWT.create(HostPageDataService.class); - hpd.load(new GerritCallback<HostPageData>() { + RpcStatus.INSTANCE = new RpcStatus(); + CallbackGroup cbg = new CallbackGroup(); + ConfigServerApi.serverInfo(cbg.add(new GerritCallback<ServerInfo>() { + @Override + public void onSuccess(ServerInfo info) { + myServerInfo = info; + } + })); + HostPageDataService hpd = GWT.create(HostPageDataService.class); + hpd.load(cbg.addFinal(new GerritCallback<HostPageData>() { @Override public void onSuccess(final HostPageData result) { Document.get().getElementById("gerrit_hostpagedata").removeFromParent(); @@ -444,7 +458,7 @@ } onModuleLoad2(result); } - }); + })); } private static void initHostname() { @@ -538,7 +552,6 @@ }; gBody.add(body); - RpcStatus.INSTANCE = new RpcStatus(); JsonUtil.addRpcStartHandler(RpcStatus.INSTANCE); JsonUtil.addRpcCompleteHandler(RpcStatus.INSTANCE); JsonUtil.setDefaultXsrfManager(new XsrfManager() { @@ -579,6 +592,7 @@ AccountApi.self().view("preferences").get(cbg.add(createMyMenuBarCallback())); } PluginLoader.load(hpd.plugins, + hpd.pluginsLoadTimeout, cbg.addFinal(new GerritCallback<VoidResult>() { @Override public void onSuccess(VoidResult result) { @@ -704,9 +718,9 @@ } if (signedIn) { - whoAmI(cfg.getAuthType() != AuthType.CLIENT_SSL_CERT_LDAP); + whoAmI(!info().auth().isClientSslCertLdap()); } else { - switch (cfg.getAuthType()) { + switch (info().auth().authType()) { case CLIENT_SSL_CERT_LDAP: break;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml index ff50ec6..36d08b1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:c='urn:import:com.google.gwtexpui.globalkey.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> .popup { position: fixed; top: 5px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java index 5fc8cb3..443a6a8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
@@ -47,7 +47,9 @@ long ageMillis = (new Date()).getTime() - when.getTime(); // shouldn't happen in a perfect world - if (ageMillis < 0) return Util.C.inTheFuture(); + if (ageMillis < 0) { + return Util.C.inTheFuture(); + } // seconds if (ageMillis < upperLimit(MINUTE_IN_MILLIS)) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java index d5805ef..94198cb 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
@@ -58,11 +58,12 @@ final SuggestBox suggestBox = new SuggestBox( new RemoteSuggestOracle(new SearchSuggestOracle()), searchBox, suggestionDisplay); - searchBox.setStyleName("gwt-TextBox"); + searchBox.setStyleName("searchTextBox"); searchBox.setVisibleLength(70); searchBox.setHintText(Gerrit.C.searchHint()); final Button searchButton = new Button(Gerrit.C.searchButton()); + searchButton.setStyleName("searchButton"); searchButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java index 90348db..c8bb731 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
@@ -16,7 +16,6 @@ import com.google.gerrit.client.account.AccountInfo; import com.google.gerrit.client.ui.InlineHyperlink; -import com.google.gerrit.reviewdb.client.AuthType; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.dom.client.Element; @@ -53,8 +52,8 @@ if (showSettingsLink) { if (Gerrit.getConfig().getSwitchAccountUrl() != null) { switchAccount.setHref(Gerrit.getConfig().getSwitchAccountUrl()); - } else if (Gerrit.getConfig().getAuthType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT - || Gerrit.getConfig().getAuthType() == AuthType.OPENID) { + } else if (Gerrit.info().auth().isDev() + || Gerrit.info().auth().isOpenId()) { switchAccount.setHref(Gerrit.selfRedirect("/login/")); } else { switchAccount.removeFromParent();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml index cd51485..8f5073d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml
@@ -19,7 +19,7 @@ xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:gerrit='urn:import:com.google.gerrit.client' xmlns:u='urn:import:com.google.gerrit.client.ui'> - <ui:style> + <ui:style gss='false'> .panel { padding: 8px; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java index 1127374..36fa98d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
@@ -18,7 +18,7 @@ import com.google.gwt.core.client.JsArray; public class AccountInfo extends JavaScriptObject { - public final native int _account_id() /*-{ return this._account_id || 0; }-*/; + public final native int _accountId() /*-{ return this._account_id || 0; }-*/; public final native String name() /*-{ return this.name; }-*/; public final native String email() /*-{ return this.email; }-*/; public final native String username() /*-{ return this.username; }-*/; @@ -29,7 +29,7 @@ * available, such as when no plugin is installed. This method returns * false if the server did not check on avatars for the account. */ - public final native boolean has_avatar_info() + public final native boolean hasAvatarInfo() /*-{ return this.hasOwnProperty('avatars') }-*/; public final AvatarInfo avatar(int sz) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java index 4b8f0e2..33d11a8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
@@ -65,7 +65,7 @@ hasContact.setStyleName(Gerrit.RESOURCES.css().accountContactOnFile()); hasContact.setVisible(false); - if (Gerrit.getConfig().isUseContactInfo()) { + if (Gerrit.info().hasContactStore()) { body.add(privhtml); body.add(hasContact); body.add(infoSecure); @@ -116,7 +116,7 @@ @Override ContactInformation toContactInformation() { final ContactInformation info; - if (Gerrit.getConfig().isUseContactInfo()) { + if (Gerrit.info().hasContactStore()) { info = new ContactInformation(); info.setAddress(addressTxt.getText()); info.setCountry(countryTxt.getText());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java index b5fe70f..2d9aa73 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -23,7 +23,6 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account.FieldName; -import com.google.gerrit.reviewdb.client.AuthType; import com.google.gerrit.reviewdb.client.ContactInformation; import com.google.gwt.core.client.JsArray; import com.google.gwt.event.dom.client.ChangeEvent; @@ -48,7 +47,8 @@ class ContactPanelShort extends Composite { protected final FlowPanel body; - protected int labelIdx, fieldIdx; + protected int labelIdx; + protected int fieldIdx; protected Button save; private String currentEmail; @@ -100,7 +100,7 @@ } int row = 0; - if (!Gerrit.getConfig().canEdit(FieldName.USER_NAME) + if (!Gerrit.info().auth().canEdit(FieldName.USER_NAME) && Gerrit.getConfig().siteHasUsernames()) { infoPlainText.resizeRows(infoPlainText.getRowCount() + 1); row(infoPlainText, row++, Util.C.userName(), new UsernameField()); @@ -167,11 +167,11 @@ } private boolean canEditFullName() { - return Gerrit.getConfig().canEdit(Account.FieldName.FULL_NAME); + return Gerrit.info().auth().canEdit(Account.FieldName.FULL_NAME); } private boolean canRegisterNewEmail() { - return Gerrit.getConfig().canEdit(Account.FieldName.REGISTER_NEW_EMAIL); + return Gerrit.info().auth().canEdit(Account.FieldName.REGISTER_NEW_EMAIL); } void hideSaveButton() { @@ -274,7 +274,7 @@ @Override public void onSuccess(EmailInfo result) { box.hide(); - if (Gerrit.getConfig().getAuthType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT) { + if (Gerrit.info().auth().isDev()) { currentEmail = addr; if (emailPick.getItemCount() == 0) { final Account me = Gerrit.getUserAccount(); @@ -324,7 +324,7 @@ buttons.add(register); buttons.add(cancel); - if (Gerrit.getConfig().getAuthType() != AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT) { + if (!Gerrit.info().auth().isDev()) { body.add(new HTML(Util.C.descRegisterNewEmail())); } body.add(inEmail);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java index 292e0b9..54541e4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
@@ -68,12 +68,44 @@ public final void ignoreWhitespace(Whitespace i) { setIgnoreWhitespaceRaw(i.toString()); } - private final native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/; public final void theme(Theme i) { setThemeRaw(i != null ? i.toString() : Theme.DEFAULT.toString()); } - private final native void setThemeRaw(String i) /*-{ this.theme = i }-*/; + + public final void showLineNumbers(boolean s) { + hideLineNumbers(!s); + } + + public final Whitespace ignoreWhitespace() { + String s = ignoreWhitespaceRaw(); + return s != null ? Whitespace.valueOf(s) : Whitespace.IGNORE_NONE; + } + + public final Theme theme() { + String s = themeRaw(); + return s != null ? Theme.valueOf(s) : Theme.DEFAULT; + } + + public final int tabSize() { + return get("tab_size", 8); + } + + public final int context() { + return get("context", 10); + } + + public final int lineLength() { + return get("line_length", 100); + } + + public final boolean showLineNumbers() { + return !hideLineNumbers(); + } + + public final boolean autoReview() { + return !manualReview(); + } public final native void tabSize(int t) /*-{ this.tab_size = t }-*/; public final native void lineLength(int c) /*-{ this.line_length = c }-*/; @@ -90,23 +122,6 @@ public final native void manualReview(boolean r) /*-{ this.manual_review = r }-*/; public final native void renderEntireFile(boolean r) /*-{ this.render_entire_file = r }-*/; public final native void hideEmptyPane(boolean s) /*-{ this.hide_empty_pane = s }-*/; - public final void showLineNumbers(boolean s) { hideLineNumbers(!s); } - - public final Whitespace ignoreWhitespace() { - String s = ignoreWhitespaceRaw(); - return s != null ? Whitespace.valueOf(s) : Whitespace.IGNORE_NONE; - } - private final native String ignoreWhitespaceRaw() /*-{ return this.ignore_whitespace }-*/; - - public final Theme theme() { - String s = themeRaw(); - return s != null ? Theme.valueOf(s) : Theme.DEFAULT; - } - private final native String themeRaw() /*-{ return this.theme }-*/; - - public final int tabSize() {return get("tab_size", 8); } - public final int context() {return get("context", 10); } - public final int lineLength() {return get("line_length", 100); } public final native boolean intralineDifference() /*-{ return this.intraline_difference || false }-*/; public final native boolean showLineEndings() /*-{ return this.show_line_endings || false }-*/; public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/; @@ -119,11 +134,12 @@ public final native boolean manualReview() /*-{ return this.manual_review || false }-*/; public final native boolean renderEntireFile() /*-{ return this.render_entire_file || false }-*/; public final native boolean hideEmptyPane() /*-{ return this.hide_empty_pane || false }-*/; - public final boolean showLineNumbers() { return !hideLineNumbers(); } - public final boolean autoReview() { return !manualReview(); } - private final native int get(String n, int d) - /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/; + private final native void setThemeRaw(String i) /*-{ this.theme = i }-*/; + private final native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/; + private final native String ignoreWhitespaceRaw() /*-{ return this.ignore_whitespace }-*/; + private final native String themeRaw() /*-{ return this.theme }-*/; + private final native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/; protected DiffPreferences() { }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java index 0908f6b..308cf30 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java
@@ -47,7 +47,7 @@ }); } - private class AgreementTable extends FancyFlexTable<ContributorAgreement> { + private static class AgreementTable extends FancyFlexTable<ContributorAgreement> { AgreementTable() { table.setWidth(""); table.setText(0, 1, Util.C.agreementStatus()); @@ -61,8 +61,9 @@ } void display(final AgreementInfo result) { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final String k : result.accepted) { addOne(result, k);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java index 1191696..b638575 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
@@ -21,7 +21,6 @@ import com.google.gerrit.client.ui.FancyFlexTable; import com.google.gerrit.common.auth.openid.OpenIdUrls; import com.google.gerrit.reviewdb.client.AccountExternalId; -import com.google.gerrit.reviewdb.client.AuthType; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; @@ -59,8 +58,8 @@ }); add(deleteIdentity); - if (Gerrit.getConfig().getAuthType() == AuthType.OPENID - || Gerrit.getConfig().getAuthType() == AuthType.OAUTH) { + if (Gerrit.info().auth().isOpenId() + || Gerrit.info().auth().isOAuth()) { Button linkIdentity = new Button(Util.C.buttonLinkIdentity()); linkIdentity.addClickHandler(new ClickHandler() { @Override @@ -180,8 +179,9 @@ } }); - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final AccountExternalId k : result) { addOneId(k);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java index 803ac55..6867bab 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
@@ -117,7 +117,8 @@ FlowPanel dateTimePanel = new FlowPanel(); - final int labelIdx, fieldIdx; + final int labelIdx; + final int fieldIdx; if (LocaleInfo.getCurrentLocale().isRTL()) { labelIdx = 1; fieldIdx = 0;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java index 52246b2..ff6fffb 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java
@@ -33,7 +33,8 @@ public class MyProfileScreen extends SettingsScreen { private AvatarImage avatar; private Anchor changeAvatar; - private int labelIdx, fieldIdx; + private int labelIdx; + private int fieldIdx; private Grid info; @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java index 67f5b4a..08effdc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java
@@ -113,8 +113,9 @@ } public void display(final List<AccountProjectWatchInfo> result) { - while (2 < table.getRowCount()) + while (2 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final AccountProjectWatchInfo k : result) { final int row = table.getRowCount();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java index 2810931..5f1e383 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
@@ -70,7 +70,7 @@ formBody.add(contactGroup); if (Gerrit.getUserAccount().getUserName() == null - && Gerrit.getConfig().canEdit(FieldName.USER_NAME)) { + && Gerrit.info().auth().canEdit(FieldName.USER_NAME)) { final FlowPanel fp = new FlowPanel(); fp.setStyleName(Gerrit.RESOURCES.css().registerScreenSection()); fp.add(new SmallHeading(Util.C.welcomeUsernameHeading())); @@ -116,7 +116,7 @@ final FlowPanel choices = new FlowPanel(); choices.setStyleName(Gerrit.RESOURCES.css().registerScreenNextLinks()); - if (Gerrit.getConfig().isUseContributorAgreements()) { + if (Gerrit.info().auth().useContributorAgreements()) { final FlowPanel agreementGroup = new FlowPanel(); agreementGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection()); agreementGroup.add(new SmallHeading(Util.C.welcomeAgreementHeading()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java index c689b49..ca4ac20 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
@@ -34,7 +34,7 @@ } link(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT); link(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS); - if (Gerrit.getConfig().isUseContributorAgreements()) { + if (Gerrit.info().auth().useContributorAgreements()) { link(Util.C.tabAgreements(), PageLinks.SETTINGS_AGREEMENTS); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java index 0cfc0984..37ad764 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java
@@ -306,8 +306,9 @@ setKeyTableVisible(false); showAddKeyBlock(true); } else { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final SshKeyInfo k : result) { addOneKey(k); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java index 9975887..e440d55 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
@@ -86,7 +86,7 @@ } private boolean canEditUserName() { - return Gerrit.getConfig().canEdit(Account.FieldName.USER_NAME); + return Gerrit.info().auth().canEdit(Account.FieldName.USER_NAME); } private void confirmSetUserName() { @@ -142,7 +142,7 @@ setUserName.setEnabled(on); } - private final class UserNameValidator implements KeyPressHandler { + private static final class UserNameValidator implements KeyPressHandler { @Override public void onKeyPress(final KeyPressEvent event) { final char code = event.getCharCode(); @@ -178,10 +178,11 @@ default: final TextBox box = (TextBox) event.getSource(); final String re; - if (box.getCursorPos() == 0) + if (box.getCursorPos() == 0) { re = Account.USER_NAME_PATTERN_FIRST; - else + } else { re = Account.USER_NAME_PATTERN_REST; + } if (!String.valueOf(code).matches("^" + re + "$")) { event.preventDefault(); event.stopPropagation();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java index 157748f..aa72300 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -250,8 +250,7 @@ if (value.getPermission(permissionName) != null) { return; } - if (Gerrit.getConfig().getWildProject() - .equals(projectAccess.getProjectName()) + if (Gerrit.info().gerrit().isAllProjects(projectAccess.getProjectName()) && !Permission.canBeOnAllProjects(value.getName(), permissionName)) { return; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml index b31e02e..52f3588 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
@@ -24,7 +24,7 @@ ui:generateLocales='default,en' > <ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> -<ui:style> +<ui:style gss='false'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java index cf3e940..1e3f918 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
@@ -248,7 +248,7 @@ for (int row = 1; row < table.getRowCount(); row++) { final AccountInfo i = getRowItem(row); if (i != null && ((CheckBox) table.getWidget(row, 1)).getValue()) { - ids.add(i._account_id()); + ids.add(i._accountId()); } } if (!ids.isEmpty()) { @@ -258,7 +258,7 @@ public void onSuccess(final VoidResult result) { for (int row = 1; row < table.getRowCount();) { final AccountInfo i = getRowItem(row); - if (i != null && ids.contains(i._account_id())) { + if (i != null && ids.contains(i._accountId())) { table.removeRow(row); } else { row++; @@ -270,8 +270,9 @@ } void display(final List<AccountInfo> result) { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final AccountInfo i : result) { final int row = table.getRowCount(); @@ -295,7 +296,7 @@ return cmp; } - return a._account_id() - b._account_id(); + return a._accountId() - b._accountId(); } public String nullToEmpty(String str) { @@ -376,8 +377,9 @@ } void display(List<GroupInfo> list) { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (final GroupInfo i : list) { final int row = table.getRowCount();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index 4446354..affbe61 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -20,7 +20,7 @@ buttonBrowseProjects = Browse projects = All projects projectRepoBrowser = Repository Browser -useContentMerge = Automatically resolve conflicts +useContentMerge = Allow content merges useContributorAgreements = Require a valid contributor agreement to upload useSignedOffBy = Require <code>Signed-off-by</code> in commit message createNewChangeForAllNotInTarget = Create a new change for every commit not in the target branch
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java index 1ffd6f0..9092508 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
@@ -44,7 +44,7 @@ public void onSuccess(ChangeInfo result) { sent = true; hide(); - Gerrit.display(PageLinks.toChange(result.legacy_id())); + Gerrit.display(PageLinks.toChange(result.legacyId())); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java index 86a31ee..0fac957 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
@@ -32,7 +32,7 @@ @Override public void onSuccess(ChangeInfo result) { Gerrit.display(Dispatcher.toEditScreen( - new PatchSet.Id(result.legacy_id(), 1), "project.config")); + new PatchSet.Id(result.legacyId(), 1), "project.config")); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java index 1b420b4..aed1dc2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
@@ -103,8 +103,9 @@ } public void displaySubset(List<GroupInfo> list, String toHighlight, int fromIndex, int toIndex) { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } Collections.sort(list, new Comparator<GroupInfo>() { @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml index ada070d..00c41dc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
@@ -24,7 +24,7 @@ ui:generateLocales='default,en' > <ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> -<ui:style> +<ui:style gss='false'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml index 4d322c0..644fef4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
@@ -25,7 +25,7 @@ ui:generateLocales='default,en' > <ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> -<ui:style> +<ui:style gss='false'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; .panel {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java index e1c73fa..4dcb52f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java
@@ -60,7 +60,7 @@ add(pluginPanel); } - private class PluginTable extends FancyFlexTable<PluginInfo> { + private static class PluginTable extends FancyFlexTable<PluginInfo> { PluginTable() { table.setText(0, 1, Util.C.columnPluginName()); table.setText(0, 2, Util.C.columnPluginSettings());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml index 120824b..0db4779 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
@@ -22,7 +22,7 @@ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' ui:generateLocales='default,en' > -<ui:style> +<ui:style gss='false'> .inheritsFrom { margin-bottom: 0.5em; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml index ab26ba8..724c7a1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
@@ -23,7 +23,7 @@ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' ui:generateLocales='default,en' > -<ui:style> +<ui:style gss='false'> @external .gwt-TextArea; .commitMessage {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java index c5f7225..a8dd9c5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -444,8 +444,9 @@ void displaySubset(List<BranchInfo> branches, int fromIndex, int toIndex) { canDelete = false; - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } for (BranchInfo k : branches.subList(fromIndex, toIndex)) { final int row = table.getRowCount(); @@ -484,8 +485,8 @@ actionsPanel.add(new Anchor(c.getLinkName(), false, c.toBranch(new Branch.NameKey(getProjectKey(), k.ref())))); } - if (k.web_links() != null) { - for (WebLinkInfo webLink : Natives.asList(k.web_links())) { + if (k.webLinks() != null) { + for (WebLinkInfo webLink : Natives.asList(k.webLinks())) { actionsPanel.add(webLink.toAnchor()); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java index 6cb9295..08ab2c0 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -38,7 +38,6 @@ import com.google.gerrit.extensions.client.InheritableBoolean; import com.google.gerrit.extensions.client.ProjectState; import com.google.gerrit.extensions.client.SubmitType; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; import com.google.gerrit.reviewdb.client.Project; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; @@ -265,7 +264,7 @@ grid.addHeader(new SmallHeading(Util.C.headingAgreements())); contributorAgreements = newInheritedBooleanBox(); - if (Gerrit.getConfig().isUseContributorAgreements()) { + if (Gerrit.info().auth().useContributorAgreements()) { saveEnabler.listenTo(contributorAgreements); grid.add(Util.C.useContributorAgreements(), contributorAgreements); } @@ -306,12 +305,12 @@ if (box.getValue(i).startsWith(InheritableBoolean.INHERIT.name())) { inheritedIndex = i; } - if (box.getValue(i).startsWith(inheritedBoolean.configured_value().name())) { + if (box.getValue(i).startsWith(inheritedBoolean.configuredValue().name())) { box.setSelectedIndex(i); } } if (inheritedIndex >= 0) { - if (getProjectKey().equals(Gerrit.getConfig().getWildProject())) { + if (Gerrit.info().gerrit().isAllProjects(getProjectKey())) { if (box.getSelectedIndex() == inheritedIndex) { for (int i = 0; i < box.getItemCount(); i++) { if (box.getValue(i).equals(InheritableBoolean.FALSE.name())) { @@ -323,7 +322,7 @@ box.removeItem(inheritedIndex); } else { box.setItemText(inheritedIndex, InheritableBoolean.INHERIT.name() + " (" - + inheritedBoolean.inherited_value() + ")"); + + inheritedBoolean.inheritedValue() + ")"); } } } @@ -342,20 +341,20 @@ void display(ConfigInfo result) { descTxt.setText(result.description()); - setBool(contributorAgreements, result.use_contributor_agreements()); - setBool(signedOffBy, result.use_signed_off_by()); - setBool(contentMerge, result.use_content_merge()); - setBool(newChangeForAllNotInTarget, result.create_new_change_for_all_not_in_target()); - setBool(requireChangeID, result.require_change_id()); - setSubmitType(result.submit_type()); + setBool(contributorAgreements, result.useContributorAgreements()); + setBool(signedOffBy, result.useSignedOffBy()); + setBool(contentMerge, result.useContentMerge()); + setBool(newChangeForAllNotInTarget, result.createNewChangeForAllNotInTarget()); + setBool(requireChangeID, result.requireChangeId()); + setSubmitType(result.submitType()); setState(result.state()); - maxObjectSizeLimit.setText(result.max_object_size_limit().configured_value()); - if (result.max_object_size_limit().inherited_value() != null) { + maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue()); + if (result.maxObjectSizeLimit().inheritedValue() != null) { effectiveMaxObjectSizeLimit.setVisible(true); effectiveMaxObjectSizeLimit.setText( - Util.M.effectiveMaxObjectSizeLimit(result.max_object_size_limit().value())); + Util.M.effectiveMaxObjectSizeLimit(result.maxObjectSizeLimit().value())); effectiveMaxObjectSizeLimit.setTitle( - Util.M.globalMaxObjectSizeLimit(result.max_object_size_limit().inherited_value())); + Util.M.globalMaxObjectSizeLimit(result.maxObjectSizeLimit().inheritedValue())); } else { effectiveMaxObjectSizeLimit.setVisible(false); } @@ -674,19 +673,16 @@ public class ProjectDownloadPanel extends DownloadPanel { public ProjectDownloadPanel(String project, boolean isAllowsAnonymous) { - super(project, null, isAllowsAnonymous); + super(project, isAllowsAnonymous); } @Override public void populateDownloadCommandLinks() { if (!urls.isEmpty()) { - if (allowedCommands.contains(DownloadCommand.CHECKOUT) - || allowedCommands.contains(DownloadCommand.DEFAULT_DOWNLOADS)) { - commands.add(cmdLinkfactory.new CloneCommandLink()); - if (Gerrit.getConfig().getSshdAddress() != null && hasUserName()) { - commands.add( - cmdLinkfactory.new CloneWithCommitMsgHookCommandLink(getProjectKey())); - } + commands.add(cmdLinkfactory.new CloneCommandLink()); + if (Gerrit.getConfig().getSshdAddress() != null && hasUserName()) { + commands.add( + cmdLinkfactory.new CloneWithCommitMsgHookCommandLink(getProjectKey())); } } } @@ -698,7 +694,7 @@ && Gerrit.getUserAccount().getUserName().length() > 0; } - private class LabeledWidgetsGrid extends FlexTable { + private static class LabeledWidgetsGrid extends FlexTable { private String labelSuffix; public LabeledWidgetsGrid() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java index 828352c..0dff684 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -186,7 +186,7 @@ private void addWebLinks(int row, ProjectInfo k) { GitwebLink gitWebLink = Gerrit.getGitwebLink(); - List<WebLinkInfo> webLinks = Natives.asList(k.web_links()); + List<WebLinkInfo> webLinks = Natives.asList(k.webLinks()); if (gitWebLink != null || (webLinks != null && !webLinks.isEmpty())) { FlowPanel p = new FlowPanel(); table.setWidget(row, ProjectsTable.C_REPO_BROWSER, p);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml index e5f6649..137ad2b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
@@ -19,7 +19,7 @@ xmlns:g='urn:import:com.google.gwt.user.client.ui' > <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/> -<ui:style> +<ui:style gss='false'> .panel { position: relative; white-space: nowrap;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java index 2b40954..b7307be 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
@@ -114,7 +114,9 @@ return e.options[e.selectedIndex].text; }, - popup: function(e){this._p=@com.google.gerrit.client.api.PopupHelper::popup(Lcom/google/gerrit/client/api/ActionContext;Lcom/google/gwt/dom/client/Element;)(this,e)}, + popup: function(e){ + this._p=@com.google.gerrit.client.api.PopupHelper::popup( + Lcom/google/gerrit/client/api/ActionContext;Lcom/google/gwt/dom/client/Element;)(this,e)}, hide: function() { this._p.@com.google.gerrit.client.api.PopupHelper::hide()(); delete this['_p']; @@ -125,11 +127,18 @@ if (m == 'get' || m == 'delete' || i==null) this[m](b); else this[m](i,b); }, - get: function(b){@com.google.gerrit.client.api.ActionContext::get(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, - post: function(i,b){@com.google.gerrit.client.api.ActionContext::post(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,i,b)}, - put: function(i,b){@com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,i,b)}, - 'delete': function(b){@com.google.gerrit.client.api.ActionContext::delete(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, - del: function(b){@com.google.gerrit.client.api.ActionContext::delete(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, + get: function(b){@com.google.gerrit.client.api.ActionContext::get( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, + post: function(i,b){@com.google.gerrit.client.api.ActionContext::post( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)( + this._u,i,b)}, + put: function(i,b){@com.google.gerrit.client.api.ActionContext::put( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)( + this._u,i,b)}, + 'delete': function(b){@com.google.gerrit.client.api.ActionContext::delete( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, + del: function(b){@com.google.gerrit.client.api.ActionContext::delete( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)}, }; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java index a5243ae..e831e8b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
@@ -42,7 +42,7 @@ ChangeInfo change, ActionInfo action, ActionButton button) { - RestApi api = ChangeApi.change(change.legacy_id().get()).view(action.id()); + RestApi api = ChangeApi.change(change.legacyId().get()).view(action.id()); JavaScriptObject f = get(action.id()); if (f != null) { ActionContext c = ActionContext.create(api);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java index 0e4048d..c092375 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
@@ -29,7 +29,7 @@ class DefaultActions { static void invoke(ChangeInfo change, ActionInfo action, RestApi api) { - invoke(action, api, callback(PageLinks.toChange(change.legacy_id()))); + invoke(action, api, callback(PageLinks.toChange(change.legacyId()))); } static void invoke(Project.NameKey project, ActionInfo action, RestApi api) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java index ebcafb8..f968fd2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
@@ -29,7 +29,7 @@ ActionInfo action, ActionButton button) { RestApi api = ChangeApi.edit( - change.legacy_id().get()) + change.legacyId().get()) .view(action.id()); JavaScriptObject f = get(action.id());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java index 931a04d..6acb420 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java
@@ -61,11 +61,18 @@ screen: function(p,c){G._screen(this.name,p,c)}, url: function (u){return G.url(this._url(u))}, - get: function(u,b){@com.google.gerrit.client.api.ActionContext::get(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, - post: function(u,i,b){@com.google.gerrit.client.api.ActionContext::post(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b)}, - put: function(u,i,b){@com.google.gerrit.client.api.ActionContext::put(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),i,b)}, - 'delete': function(u,b){@com.google.gerrit.client.api.ActionContext::delete(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, - del: function(u,b){@com.google.gerrit.client.api.ActionContext::delete(Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, + get: function(u,b){@com.google.gerrit.client.api.ActionContext::get( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, + post: function(u,i,b){@com.google.gerrit.client.api.ActionContext::post( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)( + this._api(u),i,b)}, + put: function(u,i,b){@com.google.gerrit.client.api.ActionContext::put( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)( + this._api(u),i,b)}, + 'delete': function(u,b){@com.google.gerrit.client.api.ActionContext::delete( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, + del: function(u,b){@com.google.gerrit.client.api.ActionContext::delete( + Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)}, _loadedGwt: function(){@com.google.gerrit.client.api.PluginLoader::loaded()()}, _api: function(u){return @com.google.gerrit.client.rpc.RestApi::new(Ljava/lang/String;)(this._url(u))},
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java index f0fb436..ceb0eee 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java
@@ -32,15 +32,14 @@ /** Loads JavaScript plugins with a progress meter visible. */ public class PluginLoader extends DialogBox { - private static final int MAX_LOAD_TIME_MILLIS = 5000; private static PluginLoader self; public static void load(List<String> plugins, - AsyncCallback<VoidResult> callback) { + int loadTimeout, AsyncCallback<VoidResult> callback) { if (plugins == null || plugins.isEmpty()) { callback.onSuccess(VoidResult.create()); } else { - self = new PluginLoader(callback); + self = new PluginLoader(loadTimeout, callback); self.load(plugins); self.startTimers(); self.center(); @@ -51,6 +50,7 @@ self.loadedOne(); } + private final int loadTimeout; private final AsyncCallback<VoidResult> callback; private ProgressBar progress; private Timer show; @@ -58,9 +58,10 @@ private Timer timeout; private boolean visible; - private PluginLoader(AsyncCallback<VoidResult> cb) { + private PluginLoader(int loadTimeout, AsyncCallback<VoidResult> cb) { super(/* auto hide */false, /* modal */true); callback = cb; + this.loadTimeout = loadTimeout; progress = new ProgressBar(Gerrit.C.loadingPlugins()); setStyleName(Gerrit.RESOURCES.css().errorDialog()); @@ -98,7 +99,7 @@ @Override public void run() { - progress.setValue(100 * ++cycle * 250 / MAX_LOAD_TIME_MILLIS); + progress.setValue(100 * ++cycle * 250 / loadTimeout); } }; update.scheduleRepeating(250); @@ -109,7 +110,7 @@ finish(); } }; - timeout.schedule(MAX_LOAD_TIME_MILLIS); + timeout.schedule(loadTimeout); } private void loadedOne() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java index fb489cc..d708e8c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
@@ -29,7 +29,7 @@ ActionInfo action, ActionButton button) { RestApi api = ChangeApi.revision( - change.legacy_id().get(), + change.legacyId().get(), revision.name()) .view(action.id());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml index d639150..a9e1bb3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml
@@ -19,7 +19,7 @@ xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style type='com.google.gerrit.client.change.ActionMessageBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.ActionMessageBox.Style'> @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor; .popup { background-color: trimColor; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java index edf1105..10d77cb 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
@@ -36,9 +36,8 @@ class Actions extends Composite { private static final String[] CORE = { - "abandon", "restore", "revert", "topic", - "cherrypick", "submit", "rebase", "message", - "publish", "followup", "/"}; + "abandon", "cherrypick", "followup", "hashtags", "publish", + "rebase", "restore", "revert", "submit", "topic", "/"}; interface Binder extends UiBinder<FlowPanel, Actions> {} private static final Binder uiBinder = GWT.create(Binder.class); @@ -65,7 +64,8 @@ private String message; private String branch; private String key; - private boolean canSubmit; + + private boolean rebaseParentNotCurrent = true; Actions() { initWidget(uiBinder.createAndBindUi(this)); @@ -78,20 +78,25 @@ boolean hasUser = Gerrit.isSignedIn(); RevisionInfo revInfo = info.revision(revision); CommitInfo commit = revInfo.commit(); - changeId = info.legacy_id(); + changeId = info.legacyId(); project = info.project(); subject = commit.subject(); message = commit.message(); branch = info.branch(); - key = info.change_id(); + key = info.changeId(); changeInfo = info; initChangeActions(info, hasUser); - initRevisionActions(info, revInfo, hasUser); + + NativeMap<ActionInfo> actionMap = revInfo.hasActions() + ? revInfo.actions() + : NativeMap.<ActionInfo> create(); + actionMap.copyKeysIntoChildren("id"); + reloadRevisionActions(actionMap); } private void initChangeActions(ChangeInfo info, boolean hasUser) { - NativeMap<ActionInfo> actions = info.has_actions() + NativeMap<ActionInfo> actions = info.hasActions() ? info.actions() : NativeMap.<ActionInfo> create(); actions.copyKeysIntoChildren("id"); @@ -107,34 +112,35 @@ } } - private void initRevisionActions(ChangeInfo info, RevisionInfo revInfo, - boolean hasUser) { - NativeMap<ActionInfo> actions = revInfo.has_actions() - ? revInfo.actions() - : NativeMap.<ActionInfo> create(); - actions.copyKeysIntoChildren("id"); + void reloadRevisionActions(NativeMap<ActionInfo> actions) { + if (!Gerrit.isSignedIn()) { + return; + } + boolean canSubmit = actions.containsKey("submit"); + if (canSubmit) { + ActionInfo action = actions.get("submit"); + submit.setTitle(action.title()); + submit.setEnabled(action.enabled()); + submit.setHTML(new SafeHtmlBuilder() + .openDiv() + .append(action.label()) + .closeDiv()); + submit.setEnabled(action.enabled()); + } + submit.setVisible(canSubmit); - canSubmit = false; - if (hasUser) { - canSubmit = actions.containsKey("submit"); - if (canSubmit) { - ActionInfo action = actions.get("submit"); - submit.setTitle(action.title()); - submit.setEnabled(action.enabled()); - submit.setHTML(new SafeHtmlBuilder() - .openDiv() - .append(action.label()) - .closeDiv()); - } - a2b(actions, "cherrypick", cherrypick); - a2b(actions, "rebase", rebase); - if (rebase.isVisible()) { - // it is the rebase button in RebaseDialog that the server wants to disable - rebase.setEnabled(true); - } - for (String id : filterNonCore(actions)) { - add(new ActionButton(info, revInfo, actions.get(id))); - } + a2b(actions, "cherrypick", cherrypick); + a2b(actions, "rebase", rebase); + + // The rebase button on change screen is always enabled. + // It is the "Rebase" button in the RebaseDialog that might be disabled. + rebaseParentNotCurrent = rebase.isEnabled(); + if (rebase.isVisible()) { + rebase.setEnabled(true); + } + RevisionInfo revInfo = changeInfo.revision(revision); + for (String id : filterNonCore(actions)) { + add(new ActionButton(changeInfo, revInfo, actions.get(id))); } } @@ -150,10 +156,6 @@ return ids; } - void setSubmitEnabled() { - submit.setVisible(canSubmit); - } - @UiHandler("followUp") void onFollowUp(@SuppressWarnings("unused") ClickEvent e) { if (followUpAction == null) { @@ -181,16 +183,8 @@ @UiHandler("rebase") void onRebase(@SuppressWarnings("unused") ClickEvent e) { - boolean enabled = true; - RevisionInfo revInfo = changeInfo.revision(revision); - if (revInfo.has_actions()) { - NativeMap<ActionInfo> actions = revInfo.actions(); - if (actions.containsKey("rebase")) { - enabled = actions.get("rebase").enabled(); - } - } RebaseAction.call(rebase, project, changeInfo.branch(), changeId, revision, - enabled); + rebaseParentNotCurrent); } @UiHandler("submit")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml index 40d732a..bc5a321 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
@@ -17,7 +17,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> @def BUTTON_HEIGHT 14px; #change_actions {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java index f74ebf6..3a3ffe2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java index 7245e47..09de2c8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml index d8236e6..c3539bc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml
@@ -19,7 +19,7 @@ xmlns:u='urn:import:com.google.gerrit.client.ui' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> .cancel { float: right; } </ui:style> <g:HTMLPanel>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java index 4d7dac2..4133c13 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
@@ -113,6 +113,7 @@ String label_need(); String replyBox(); String selected(); + String highlight(); String hashtagName(); } @@ -131,11 +132,10 @@ private String base; private String revision; private ChangeInfo changeInfo; + private boolean hasDraftComments; private CommentLinkProcessor commentLinkProcessor; private EditInfo edit; - private KeyCommandSet keysNavigation; - private KeyCommandSet keysAction; private List<HandlerRegistration> handlers = new ArrayList<>(4); private UpdateCheckTimer updateCheck; private Timestamp lastDisplayedUpdate; @@ -248,7 +248,7 @@ void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) { RestApi call = ChangeApi.detail(changeId.get()); ChangeList.addOptions(call, EnumSet.of( - ListChangesOption.CURRENT_ACTIONS, + ListChangesOption.CHANGE_ACTIONS, ListChangesOption.ALL_REVISIONS)); if (!fg) { call.background(); @@ -256,6 +256,18 @@ call.get(cb); } + void loadRevisionInfo() { + RestApi call = ChangeApi.actions(changeId.get(), revision); + call.background(); + call.get(new GerritCallback<NativeMap<ActionInfo>>() { + @Override + public void onSuccess(NativeMap<ActionInfo> actionMap) { + actionMap.copyKeysIntoChildren("id"); + renderRevisionInfo(changeInfo, actionMap); + } + }); + } + @Override protected void onUnload() { if (replyAction != null) { @@ -281,84 +293,24 @@ labels.init(style); reviewers.init(style, ccText); hashtags.init(style); - - keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation()); - keysNavigation.add(new KeyCommand(0, 'u', Util.C.upToChangeList()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - Gerrit.displayLastChangeList(); - } - }); - keysNavigation.add(new KeyCommand(0, 'R', Util.C.keyReloadChange()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - Gerrit.display(PageLinks.toChange(changeId)); - } - }); - keysNavigation.add(new KeyCommand(0, 'n', Util.C.keyNextPatchSet()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - gotoSibling(1); - } - }, new KeyCommand(0, 'p', Util.C.keyPreviousPatchSet()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - gotoSibling(-1); - } - }); - - keysAction = new KeyCommandSet(Gerrit.C.sectionActions()); - keysAction.add(new KeyCommand(0, 'a', Util.C.keyPublishComments()) { - @Override - public void onKeyPress(KeyPressEvent event) { - if (Gerrit.isSignedIn()) { - onReply(null); - } else { - Gerrit.doSignIn(getToken()); - } - } - }); - keysAction.add(new KeyCommand(0, 'x', Util.C.keyExpandAllMessages()) { - @Override - public void onKeyPress(KeyPressEvent event) { - onExpandAll(null); - } - }); - keysAction.add(new KeyCommand(0, 'z', Util.C.keyCollapseAllMessages()) { - @Override - public void onKeyPress(KeyPressEvent event) { - onCollapseAll(null); - } - }); - if (Gerrit.isSignedIn()) { - keysAction.add(new KeyCommand(0, 's', Util.C.changeTableStar()) { - @Override - public void onKeyPress(KeyPressEvent event) { - star.setValue(!star.getValue(), true); - } - }); - keysAction.add(new KeyCommand(0, 'c', Util.C.keyAddReviewers()) { - @Override - public void onKeyPress(KeyPressEvent event) { - reviewers.onOpenForm(); - } - }); - } } private void initReplyButton(ChangeInfo info, String revision) { - if (!info.revision(revision).is_edit()) { + if (!info.revision(revision).isEdit()) { reply.setTitle(Gerrit.getConfig().getReplyTitle()); reply.setHTML(new SafeHtmlBuilder() .openDiv() .append(Gerrit.getConfig().getReplyLabel()) .closeDiv()); + if (hasDraftComments) { + reply.setStyleName(style.highlight()); + } reply.setVisible(true); } } private void gotoSibling(final int offset) { - if (offset > 0 && changeInfo.current_revision().equals(revision)) { + if (offset > 0 && changeInfo.currentRevision().equals(revision)) { return; } @@ -372,7 +324,7 @@ if (revision.equals(revisions.get(i).name())) { if (0 <= i + offset && i + offset < revisions.length()) { Gerrit.display(PageLinks.toChange( - new PatchSet.Id(changeInfo.legacy_id(), + new PatchSet.Id(changeInfo.legacyId(), revisions.get(i + offset)._number()))); return; } @@ -384,7 +336,7 @@ private void initIncludedInAction(ChangeInfo info) { if (info.status() == Status.MERGED) { includedInAction = new IncludedInAction( - info.legacy_id(), + info.legacyId(), style, headerLine, includedIn); includedIn.setVisible(true); } @@ -392,7 +344,7 @@ private void initChangeAction(ChangeInfo info) { if (info.status() == Status.DRAFT) { - NativeMap<ActionInfo> actions = info.has_actions() + NativeMap<ActionInfo> actions = info.hasActions() ? info.actions() : NativeMap.<ActionInfo> create(); actions.copyKeysIntoChildren("id"); @@ -403,11 +355,12 @@ } } - private void initRevisionsAction(ChangeInfo info, String revision) { + private void initRevisionsAction(ChangeInfo info, String revision, + NativeMap<ActionInfo> actions) { int currentPatchSet; - if (info.current_revision() != null - && info.revisions().containsKey(info.current_revision())) { - currentPatchSet = info.revision(info.current_revision())._number(); + if (info.currentRevision() != null + && info.revisions().containsKey(info.currentRevision())) { + currentPatchSet = info.revision(info.currentRevision())._number(); } else { JsArray<RevisionInfo> revList = info.revisions().values(); RevisionInfo.sortRevisionInfoByNumber(revList); @@ -426,16 +379,11 @@ patchSetsText.setInnerText(Resources.M.patchSets( currentlyViewedPatchSet, currentPatchSet)); patchSetsAction = new PatchSetsAction( - info.legacy_id(), revision, edit, + info.legacyId(), revision, edit, style, headerLine, patchSets); RevisionInfo revInfo = info.revision(revision); if (revInfo.draft()) { - NativeMap<ActionInfo> actions = revInfo.has_actions() - ? revInfo.actions() - : NativeMap.<ActionInfo> create(); - actions.copyKeysIntoChildren("id"); - if (actions.containsKey("publish")) { publish.setVisible(true); publish.setTitle(actions.get("publish").title()); @@ -454,20 +402,20 @@ private void initProjectLinks(final ChangeInfo info) { projectSettingsLink.setHref( - "#" + PageLinks.toProject(info.project_name_key())); + "#" + PageLinks.toProject(info.projectNameKey())); projectSettings.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (Hyperlink.impl.handleAsClick((Event) event.getNativeEvent())) { event.stopPropagation(); event.preventDefault(); - Gerrit.display(PageLinks.toProject(info.project_name_key())); + Gerrit.display(PageLinks.toProject(info.projectNameKey())); } } }, ClickEvent.getType()); projectDashboard.setText(info.project()); projectDashboard.setTargetHistoryToken( - PageLinks.toProjectDefaultDashboard(info.project_name_key())); + PageLinks.toProjectDefaultDashboard(info.projectNameKey())); } private void initBranchLink(ChangeInfo info) { @@ -475,7 +423,7 @@ branchLink.setTargetHistoryToken( PageLinks.toChangeQuery( BranchLink.query( - info.project_name_key(), + info.projectNameKey(), info.status(), info.branch(), null))); @@ -505,7 +453,7 @@ reviewMode.setVisible(false); } - if (rev.is_edit()) { + if (rev.isEdit()) { if (info.hasEditBasedOnCurrentPatchSet()) { publishEdit.setVisible(true); } else { @@ -517,11 +465,11 @@ } private boolean isEditModeEnabled(ChangeInfo info, RevisionInfo rev) { - if (rev.is_edit()) { + if (rev.isEdit()) { return true; } if (edit == null) { - return revision.equals(info.current_revision()); + return revision.equals(info.currentRevision()); } return rev._number() == RevisionInfo.findEditParent( info.revisions().values()); @@ -566,7 +514,94 @@ @Override public void registerKeys() { super.registerKeys(); + + KeyCommandSet keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation()); + keysNavigation.add(new KeyCommand(0, 'u', Util.C.upToChangeList()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + Gerrit.displayLastChangeList(); + } + }); + keysNavigation.add(new KeyCommand(0, 'R', Util.C.keyReloadChange()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + Gerrit.display(PageLinks.toChange(changeId)); + } + }); + keysNavigation.add(new KeyCommand(0, 'n', Util.C.keyNextPatchSet()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + gotoSibling(1); + } + }, new KeyCommand(0, 'p', Util.C.keyPreviousPatchSet()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + gotoSibling(-1); + } + }); handlers.add(GlobalKey.add(this, keysNavigation)); + + KeyCommandSet keysAction = new KeyCommandSet(Gerrit.C.sectionActions()); + keysAction.add(new KeyCommand(0, 'a', Util.C.keyPublishComments()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + onReply(null); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 'x', Util.C.keyExpandAllMessages()) { + @Override + public void onKeyPress(KeyPressEvent event) { + onExpandAll(null); + } + }); + keysAction.add(new KeyCommand(0, 'z', Util.C.keyCollapseAllMessages()) { + @Override + public void onKeyPress(KeyPressEvent event) { + onCollapseAll(null); + } + }); + keysAction.add(new KeyCommand(0, 's', Util.C.changeTableStar()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + star.setValue(!star.getValue(), true); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 'c', Util.C.keyAddReviewers()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + reviewers.onOpenForm(); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 't', Util.C.keyEditTopic()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + // In Firefox this event is mistakenly called when F5 is pressed so + // differentiate F5 from 't' by checking the charCode(F5=0, t=116). + if (event.getNativeEvent().getCharCode() == 0) { + Window.Location.reload(); + return; + } + if (topic.canEdit()) { + topic.onEdit(); + } + } else { + Gerrit.doSignIn(getToken()); + } + } + }); handlers.add(GlobalKey.add(this, keysAction)); files.registerKeys(); } @@ -746,9 +781,9 @@ private void loadConfigInfo(final ChangeInfo info, final String base) { info.revisions().copyKeysIntoChildren("name"); if (edit != null) { - edit.set_name(edit.commit().commit()); - info.set_edit(edit); - if (edit.has_files()) { + edit.setName(edit.commit().commit()); + info.setEdit(edit); + if (edit.hasFiles()) { edit.files().copyKeysIntoChildren("path"); } info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit)); @@ -766,14 +801,14 @@ if (revision == null) { RevisionInfo.sortRevisionInfoByNumber(list); RevisionInfo rev = list.get(list.length() - 1); - if (rev.is_edit()) { - info.set_current_revision(rev.name()); + if (rev.isEdit()) { + info.setCurrentRevision(rev.name()); } } else if (revision.equals("edit") || revision.equals("0")) { for (int i = 0; i < list.length(); i++) { RevisionInfo r = list.get(i); - if (r.is_edit()) { - info.set_current_revision(r.name()); + if (r.isEdit()) { + info.setCurrentRevision(r.name()); break; } } @@ -784,7 +819,7 @@ CallbackGroup group = new CallbackGroup(); Timestamp lastReply = myLastReply(info); - if (rev.is_edit()) { + if (rev.isEdit()) { loadFileList(b, rev, lastReply, group, null, null); } else { loadDiff(b, rev, lastReply, group); @@ -798,7 +833,7 @@ RevisionInfoCache.add(changeId, rev); ConfigInfoCache.add(info); - ConfigInfoCache.get(info.project_name_key(), + ConfigInfoCache.get(info.projectNameKey(), group.addFinal(new ScreenLoadCallback<ConfigInfoCache.Entry>(this) { @Override protected void preDisplay(Entry result) { @@ -806,16 +841,17 @@ commentLinkProcessor = result.getCommentLinkProcessor(); setTheme(result.getTheme()); renderChangeInfo(info); + loadRevisionInfo(); } })); } static Timestamp myLastReply(ChangeInfo info) { if (Gerrit.isSignedIn() && info.messages() != null) { - int self = Gerrit.getUserAccountInfo()._account_id(); + int self = Gerrit.getUserAccountInfo()._accountId(); for (int i = info.messages().length() - 1; i >= 0; i--) { MessageInfo m = info.messages().get(i); - if (m.author() != null && m.author()._account_id() == self) { + if (m.author() != null && m.author()._accountId() == self) { return m.date(); } } @@ -872,16 +908,19 @@ } private List<NativeMap<JsArray<CommentInfo>>> loadComments( - RevisionInfo rev, CallbackGroup group) { - final int id = rev._number(); + final RevisionInfo rev, CallbackGroup group) { final List<NativeMap<JsArray<CommentInfo>>> r = new ArrayList<>(1); - ChangeApi.revision(changeId.get(), rev.name()) - .view("comments") + // TODO(dborowitz): Could eliminate this call by adding an option to include + // inline comments in the change detail. + ChangeApi.comments(changeId.get()) .get(group.add(new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() { @Override public void onSuccess(NativeMap<JsArray<CommentInfo>> result) { - r.add(result); - history.addComments(id, result); + // Return value is used for populating the file table, so only count + // comments for the current revision. Still include all comments in + // the history table. + r.add(filterForRevision(result, rev._number())); + history.addComments(result); } @Override @@ -891,6 +930,23 @@ return r; } + private static NativeMap<JsArray<CommentInfo>> filterForRevision( + NativeMap<JsArray<CommentInfo>> comments, int id) { + NativeMap<JsArray<CommentInfo>> filtered = NativeMap.create(); + for (String k : comments.keySet()) { + JsArray<CommentInfo> allRevisions = comments.get(k); + JsArray<CommentInfo> thisRevision = JsArray.createArray().cast(); + for (int i = 0; i < allRevisions.length(); i++) { + CommentInfo c = allRevisions.get(i); + if (c.patchSet() == id) { + thisRevision.push(c); + } + } + filtered.put(k, thisRevision); + } + return filtered; + } + private List<NativeMap<JsArray<CommentInfo>>> loadDrafts( RevisionInfo rev, CallbackGroup group) { final List<NativeMap<JsArray<CommentInfo>>> r = new ArrayList<>(1); @@ -901,6 +957,7 @@ @Override public void onSuccess(NativeMap<JsArray<CommentInfo>> result) { r.add(result); + hasDraftComments = !result.isEmpty(); } @Override @@ -914,7 +971,7 @@ } private void loadCommit(final RevisionInfo rev, CallbackGroup group) { - if (rev.is_edit()) { + if (rev.isEdit()) { return; } @@ -922,7 +979,7 @@ group.add(new AsyncCallback<CommitInfo>() { @Override public void onSuccess(CommitInfo info) { - rev.set_commit(info); + rev.setCommit(info); } @Override @@ -933,7 +990,6 @@ private void loadSubmitType(final Change.Status status, final boolean canSubmit) { if (canSubmit) { - actions.setSubmitEnabled(); if (status == Change.Status.NEW) { statusText.setInnerText(Util.C.readyToSubmit()); } @@ -963,7 +1019,7 @@ private RevisionInfo resolveRevisionToDisplay(ChangeInfo info) { RevisionInfo rev = resolveRevisionOrPatchSetId(info, revision, - info.current_revision()); + info.currentRevision()); if (rev != null) { revision = rev.name(); return rev; @@ -980,7 +1036,7 @@ return rev; } else { new ErrorDialog( - Resources.M.changeWithNoRevisions(info.legacy_id().get())).center(); + Resources.M.changeWithNoRevisions(info.legacyId().get())).center(); throw new IllegalStateException("no revision, cannot proceed"); } } @@ -1043,18 +1099,7 @@ private void renderChangeInfo(ChangeInfo info) { changeInfo = info; lastDisplayedUpdate = info.updated(); - RevisionInfo revisionInfo = info.revision(revision); - boolean current = revision.equals(info.current_revision()) - && !revisionInfo.is_edit(); - if (revisionInfo.is_edit()) { - statusText.setInnerText(Util.C.changeEdit()); - } else if (!current) { - statusText.setInnerText(Util.C.notCurrent()); - labels.setVisible(false); - } else { - statusText.setInnerText(Util.toLongString(info.status())); - } labels.set(info); renderOwner(info); @@ -1063,7 +1108,6 @@ initReplyButton(info, revision); initIncludedInAction(info); initChangeAction(info); - initRevisionsAction(info, revision); initDownloadAction(info, revision); initProjectLinks(info); initBranchLink(info); @@ -1072,7 +1116,7 @@ star.setValue(info.starred()); permalink.setHref(ChangeLink.permalink(changeId)); - permalink.setText(String.valueOf(info.legacy_id())); + permalink.setText(String.valueOf(info.legacyId())); topic.set(info, revision); commit.set(commentLinkProcessor, info, revision); related.set(info, revision); @@ -1083,23 +1127,45 @@ setVisible(hashtagTableRow, false); } + StringBuilder sb = new StringBuilder(); + sb.append(Util.M.changeScreenTitleId(info.idAbbreviated())); + if (info.subject() != null) { + sb.append(": "); + sb.append(info.subject()); + } + setWindowTitle(sb.toString()); + + // Although this is related to the revision, we can process it early to + // render it faster. + if (!info.status().isOpen() + || !revision.equals(info.currentRevision()) + || info.revision(revision).isEdit()) { + setVisible(strategy, false); + } + + // Properly render revision actions initially while waiting for + // the callback to populate them correctly. + NativeMap<ActionInfo> emptyMap = NativeMap.<ActionInfo> create(); + initRevisionsAction(info, revision, emptyMap); + quickApprove.setVisible(false); + actions.reloadRevisionActions(emptyMap); + + RevisionInfo revisionInfo = info.revision(revision); + boolean current = revision.equals(info.currentRevision()) + && !revisionInfo.isEdit(); + + if (revisionInfo.isEdit()) { + statusText.setInnerText(Util.C.changeEdit()); + } else if (!current) { + statusText.setInnerText(Util.C.notCurrent()); + labels.setVisible(false); + } else { + statusText.setInnerText(Util.toLongString(info.status())); + } + if (Gerrit.isSignedIn()) { - replyAction = new ReplyAction(info, revision, + replyAction = new ReplyAction(info, revision, hasDraftComments, style, commentLinkProcessor, reply, quickApprove); - if (topic.canEdit()) { - keysAction.add(new KeyCommand(0, 't', Util.C.keyEditTopic()) { - @Override - public void onKeyPress(KeyPressEvent event) { - // In Firefox this event is mistakenly called when F5 is pressed so - // differentiate F5 from 't' by checking the charCode(F5=0, t=116). - if (event.getNativeEvent().getCharCode() == 0) { - Window.Location.reload(); - return; - } - topic.onEdit(); - } - }); - } } history.set(commentLinkProcessor, replyAction, changeId, info); @@ -1108,16 +1174,15 @@ loadSubmitType(info.status(), isSubmittable(info)); } else { quickApprove.setVisible(false); - setVisible(strategy, false); } + } - StringBuilder sb = new StringBuilder(); - sb.append(Util.M.changeScreenTitleId(info.id_abbreviated())); - if (info.subject() != null) { - sb.append(": "); - sb.append(info.subject()); - } - setWindowTitle(sb.toString()); + private void renderRevisionInfo(ChangeInfo info, + NativeMap<ActionInfo> actionMap) { + initRevisionsAction(info, revision, actionMap); + commit.setParentNotCurrent(actionMap.containsKey("rebase") + && actionMap.get("rebase").enabled()); + actions.reloadRevisionActions(actionMap); } private void renderOwner(ChangeInfo info) { @@ -1138,7 +1203,7 @@ ? info.owner().name() : info.owner().email() != null ? info.owner().email() - : String.valueOf(info.owner()._account_id()), Change.Status.NEW)); + : String.valueOf(info.owner()._accountId()), Change.Status.NEW)); } private void renderSubmitType(String action) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml index 6f8d3aa..830369d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
@@ -21,7 +21,7 @@ xmlns:x='urn:import:com.google.gerrit.client.ui'> <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style type='com.google.gerrit.client.change.ChangeScreen.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.ChangeScreen.Style'> @eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor; @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java index bcd8f6b..357f04c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
@@ -47,7 +47,7 @@ @Override public void onSend() { - ChangeApi.cherrypick(info.legacy_id().get(), revision, + ChangeApi.cherrypick(info.legacyId().get(), revision, getDestinationBranch(), getMessageText(), new GerritCallback<ChangeInfo>() { @@ -55,7 +55,7 @@ public void onSuccess(ChangeInfo result) { sent = true; hide(); - Gerrit.display(PageLinks.toChange(result.legacy_id())); + Gerrit.display(PageLinks.toChange(result.legacyId())); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java index 93db1a7..f75f6a1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -19,13 +19,11 @@ import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.GitwebLink; import com.google.gerrit.client.WebLinkInfo; -import com.google.gerrit.client.actions.ActionInfo; import com.google.gerrit.client.account.AccountInfo; import com.google.gerrit.client.changes.ChangeInfo; import com.google.gerrit.client.changes.ChangeInfo.CommitInfo; import com.google.gerrit.client.changes.ChangeInfo.GitPerson; import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo; -import com.google.gerrit.client.rpc.NativeMap; import com.google.gerrit.client.rpc.Natives; import com.google.gerrit.client.ui.CommentLinkProcessor; import com.google.gerrit.client.ui.InlineHyperlink; @@ -111,8 +109,8 @@ CommitInfo commit = revInfo.commit(); commitName.setText(revision); - idText.setText("Change-Id: " + change.change_id()); - idText.setPreviewText(change.change_id()); + idText.setText("Change-Id: " + change.changeId()); + idText.setPreviewText(change.changeId()); formatLink(commit.author(), authorPanel, authorNameEmail, authorDate, change); @@ -127,15 +125,10 @@ } setParents(change.project(), revInfo.commit().parents()); + } + void setParentNotCurrent(boolean parentNotCurrent) { // display the orange ball if parent has moved on (not current) - boolean parentNotCurrent = false; - if (revInfo.has_actions()) { - NativeMap<ActionInfo> actions = revInfo.actions(); - if (actions.containsKey("rebase")) { - parentNotCurrent = actions.get("rebase").enabled(); - } - } UIObject.setVisible(parentNotCurrentText, parentNotCurrent); parentNotCurrentText.setInnerText(parentNotCurrent ? "\u25CF" : ""); } @@ -148,7 +141,7 @@ gw.getLinkName()); } - JsArray<WebLinkInfo> links = revInfo.commit().web_links(); + JsArray<WebLinkInfo> links = revInfo.commit().webLinks(); if (links != null) { for (WebLinkInfo link : Natives.asList(links)) { webLinkPanel.add(link.toAnchor()); @@ -198,7 +191,7 @@ a.setStyleName(style.parentWebLink()); panel.add(a); } - JsArray<WebLinkInfo> links = c.web_links(); + JsArray<WebLinkInfo> links = c.webLinks(); if (links != null) { for (WebLinkInfo link : Natives.asList(links)) { panel.add(link.toAnchor()); @@ -219,7 +212,7 @@ // only try to fetch the avatar image for author and committer if an avatar // plugin is installed, if the change owner has no avatar info assume that // no avatar plugin is installed - if (change.owner().has_avatar_info()) { + if (change.owner().hasAvatarInfo()) { AvatarImage avatar; if (change.owner().email().equals(person.email())) { avatar = new AvatarImage(change.owner());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml index 93312fa..5f476be 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
@@ -21,7 +21,7 @@ xmlns:clippy='urn:import:com.google.gwtexpui.clippy.client'> <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/> <ui:image field="toggle" src="moreLess.png"/> - <ui:style type='com.google.gerrit.client.change.CommitBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.CommitBox.Style'> @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor; .collapsed .scroll { height: 250px }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java index ee94564..ce17013 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2015 The Android Open Source Project +// Copyright (C) 2015 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java index e55b7ed..aa2b6f0 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2015 The Android Open Source Project +// Copyright (C) 2015 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml index 4e7b2ba..9e79f752 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml
@@ -19,7 +19,7 @@ xmlns:u='urn:import:com.google.gerrit.client.ui' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> .cancel { float: right; } </ui:style> <g:HTMLPanel>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java index b1bb4e0..34e67dd 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
@@ -30,7 +30,7 @@ Widget downloadButton) { super(style, relativeTo, downloadButton); this.downloadBox = new DownloadBox(info, revision, - new PatchSet.Id(info.legacy_id(), + new PatchSet.Id(info.legacyId(), info.revision(revision)._number())); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java index 49389f3..0303a88 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -80,7 +80,7 @@ protected void onLoad() { if (fetch == null) { if (psId.get() == 0) { - ChangeApi.editWithCommands(change.legacy_id().get()).get( + ChangeApi.editWithCommands(change.legacyId().get()).get( new AsyncCallback<EditInfo>() { @Override public void onSuccess(EditInfo result) { @@ -93,9 +93,9 @@ } }); } else { - RestApi call = ChangeApi.detail(change.legacy_id().get()); + RestApi call = ChangeApi.detail(change.legacyId().get()); ChangeList.addOptions(call, EnumSet.of( - revision.equals(change.current_revision()) + revision.equals(change.currentRevision()) ? ListChangesOption.CURRENT_REVISION : ListChangesOption.ALL_REVISIONS, ListChangesOption.DOWNLOAD_COMMANDS)); @@ -268,7 +268,7 @@ if (scheme != null && scheme != pref.getDownloadUrl()) { pref.setDownloadUrl(scheme); PreferenceInput in = PreferenceInput.create(); - in.download_scheme(scheme); + in.downloadScheme(scheme); AccountApi.self().view("preferences") .put(in, new AsyncCallback<JavaScriptObject>() { @Override @@ -303,11 +303,11 @@ return createObject().cast(); } - final void download_scheme(DownloadScheme s) { - download_scheme0(s.name()); + final void downloadScheme(DownloadScheme s) { + downloadScheme0(s.name()); } - private final native void download_scheme0(String n) /*-{ + private final native void downloadScheme0(String n) /*-{ this.download_scheme = n; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java index 7172011..e73c70a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java
@@ -26,8 +26,6 @@ import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTMLPanel; -import java.util.Collections; -import java.util.Comparator; import java.util.List; class FileComments extends Composite { @@ -38,22 +36,15 @@ @UiField FlowPanel comments; FileComments(CommentLinkProcessor clp, - PatchSet.Id ps, + PatchSet.Id defaultPs, String title, List<CommentInfo> list) { initWidget(uiBinder.createAndBindUi(this)); - path.setTargetHistoryToken(url(ps, list.get(0))); + path.setTargetHistoryToken(url(defaultPs, list.get(0))); path.setText(title); - - Collections.sort(list, new Comparator<CommentInfo>() { - @Override - public int compare(CommentInfo a, CommentInfo b) { - return a.line() - b.line(); - } - }); for (CommentInfo c : list) { - comments.add(new LineComment(clp, ps, c)); + comments.add(new LineComment(clp, defaultPs, c)); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml index 5de04cc..e463e95 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:c='urn:import:com.google.gerrit.client.ui' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> .box { } .path {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java index 2947be8..dd8df36 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -87,7 +87,7 @@ String deltaColumn2(); String inserted(); String deleted(); - String removeButton(); + String restoreDelete(); } public static enum Mode { @@ -516,8 +516,8 @@ for (int i = 0; i < list.length(); i++) { FileInfo info = list.get(i); if (!Patch.COMMIT_MSG.equals(info.path()) && !info.binary()) { - inserted += info.lines_inserted(); - deleted += info.lines_deleted(); + inserted += info.linesInserted(); + deleted += info.linesDeleted(); } } } @@ -548,7 +548,7 @@ if (mode == Mode.REVIEW) { sb.openTh().setStyleName(R.css().reviewed()).closeTh(); } else { - sb.openTh().setStyleName(R.css().removeButton()).closeTh(); + sb.openTh().setStyleName(R.css().restoreDelete()).closeTh(); } sb.openTh().setStyleName(R.css().status()).closeTh(); sb.openTh().append(Util.C.patchTableColumnName()).closeTh(); @@ -592,20 +592,26 @@ } private void columnDeleteRestore(SafeHtmlBuilder sb, FileInfo info) { - sb.openTd().setStyleName(R.css().removeButton()); + sb.openTd().setStyleName(R.css().restoreDelete()); if (hasUser) { if (!Patch.COMMIT_MSG.equals(info.path())) { boolean editable = isEditable(info); - sb.openElement("button") - .setAttribute("title", editable - ? Resources.C.removeFileInline() - : Resources.C.restoreFileInline()) - .setAttribute("onclick", (editable ? DELETE : RESTORE) - + "(event," + info._row() + ")") - .append(new ImageResourceRenderer().render(editable - ? Gerrit.RESOURCES.redNot() - : Gerrit.RESOURCES.editUndo())) + sb.openDiv() + .openElement("button") + .setAttribute("title", Resources.C.restoreFileInline()) + .setAttribute("onclick", RESTORE + "(event," + info._row() + ")") + .append(new ImageResourceRenderer().render( + Gerrit.RESOURCES.editUndo())) .closeElement("button"); + if (editable) { + sb.openElement("button") + .setAttribute("title", Resources.C.removeFileInline()) + .setAttribute("onclick", DELETE + "(event," + info._row() + ")") + .append(new ImageResourceRenderer().render( + Gerrit.RESOURCES.redNot())) + .closeElement("button"); + } + sb.closeDiv(); } } sb.closeTd(); @@ -657,10 +663,10 @@ } sb.closeAnchor(); - if (info.old_path() != null) { + if (info.oldPath() != null) { sb.br(); sb.openSpan().setStyleName(R.css().renameCopySource()) - .append(info.old_path()) + .append(info.oldPath()) .closeSpan(); } sb.closeTd(); @@ -733,16 +739,16 @@ sb.openTd().setStyleName(R.css().deltaColumn1()); if (!Patch.COMMIT_MSG.equals(info.path()) && !info.binary()) { if (showChangeSizeBars) { - sb.append(info.lines_inserted() + info.lines_deleted()); + sb.append(info.linesInserted() + info.linesDeleted()); } else if (!ChangeType.DELETED.matches(info.status())) { if (ChangeType.ADDED.matches(info.status())) { - sb.append(info.lines_inserted()) + sb.append(info.linesInserted()) .append(" lines"); } else { sb.append("+") - .append(info.lines_inserted()) + .append(info.linesInserted()) .append(", -") - .append(info.lines_deleted()); + .append(info.linesDeleted()); } } } @@ -753,24 +759,24 @@ sb.openTd().setStyleName(R.css().deltaColumn2()); if (showChangeSizeBars && !Patch.COMMIT_MSG.equals(info.path()) && !info.binary() - && (info.lines_inserted() != 0 || info.lines_deleted() != 0)) { + && (info.linesInserted() != 0 || info.linesDeleted() != 0)) { int w = 80; int t = inserted + deleted; - int i = Math.max(5, (int) (((double) w) * info.lines_inserted() / t)); - int d = Math.max(5, (int) (((double) w) * info.lines_deleted() / t)); + int i = Math.max(5, (int) (((double) w) * info.linesInserted() / t)); + int d = Math.max(5, (int) (((double) w) * info.linesDeleted() / t)); sb.setAttribute( "title", - Util.M.patchTableSize_LongModify(info.lines_inserted(), - info.lines_deleted())); + Util.M.patchTableSize_LongModify(info.linesInserted(), + info.linesDeleted())); - if (0 < info.lines_inserted()) { + if (0 < info.linesInserted()) { sb.openDiv() .setStyleName(R.css().inserted()) .setAttribute("style", "width:" + i + "px") .closeDiv(); } - if (0 < info.lines_deleted()) { + if (0 < info.linesDeleted()) { sb.openDiv() .setStyleName(R.css().deleted()) .setAttribute("style", "width:" + d + "px") @@ -786,7 +792,7 @@ if (mode == Mode.REVIEW) { sb.openTh().setStyleName(R.css().reviewed()).closeTh(); } else { - sb.openTh().setStyleName(R.css().removeButton()).closeTh(); + sb.openTh().setStyleName(R.css().restoreDelete()).closeTh(); } sb.openTh().setStyleName(R.css().status()).closeTh(); sb.openTd().closeTd(); // path
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java index 5a7df72..30394d6 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
@@ -39,7 +39,7 @@ new GerritCallback<ChangeInfo>() { @Override public void onSuccess(ChangeInfo result) { - Gerrit.display(PageLinks.toChange(result.legacy_id())); + Gerrit.display(PageLinks.toChange(result.legacyId())); hide(); } });
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java index faba10d..d2afbcf 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
@@ -51,6 +51,8 @@ private static final String REMOVE; private static final String DATA_ID = "data-id"; + private boolean canEdit; + static { REMOVE = DOM.createUniqueId().replace('-', '_'); init(REMOVE); @@ -121,9 +123,10 @@ } void set(ChangeInfo info) { - this.changeId = info.legacy_id(); + canEdit = info.hasActions() && info.actions().containsKey("hashtags"); + this.changeId = info.legacyId(); display(info); - openForm.setVisible(Gerrit.isSignedIn()); + openForm.setVisible(canEdit); } @UiHandler("openForm") @@ -165,13 +168,15 @@ "#" + PageLinks.toChangeQuery("hashtag:\"" + hashtagName + "\"")) .setAttribute("role", "listitem") .append("#").append(hashtagName) - .closeAnchor() - .openElement("button") - .setAttribute("title", "Remove hashtag") - .setAttribute("onclick", REMOVE + "(event)") - .append("×") - .closeElement("button") - .closeSpan(); + .closeAnchor(); + if (canEdit) { + html.openElement("button") + .setAttribute("title", "Remove hashtag") + .setAttribute("onclick", REMOVE + "(event)") + .append("×") + .closeElement("button"); + } + html.closeSpan(); if (itr.hasNext()) { html.append(' '); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml index dd06c77..ba4d6cc 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml
@@ -19,7 +19,7 @@ xmlns:c='urn:import:com.google.gwtexpui.globalkey.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> button.openAdd { margin: 3px 3px 0 0; float: right;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java index 7635d81..47a870b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
@@ -14,8 +14,6 @@ package com.google.gerrit.client.change; -import com.google.gerrit.client.account.AccountInfo; -import com.google.gerrit.client.changes.ChangeApi; import com.google.gerrit.client.changes.ChangeInfo; import com.google.gerrit.client.changes.ChangeInfo.MessageInfo; import com.google.gerrit.client.changes.CommentInfo; @@ -23,9 +21,7 @@ import com.google.gerrit.client.rpc.Natives; import com.google.gerrit.client.ui.CommentLinkProcessor; import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gwt.core.client.JsArray; -import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; @@ -33,22 +29,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; class History extends FlowPanel { private CommentLinkProcessor clp; private ReplyAction replyAction; private Change.Id changeId; - private final Set<Integer> loaded = new HashSet<>(); - private final Map<AuthorRevision, List<CommentInfo>> byAuthor = - new HashMap<>(); - - private final List<Integer> toLoad = new ArrayList<>(4); - private int active; + private final Map<Integer, List<CommentInfo>> byAuthor = new HashMap<>(); void set(CommentLinkProcessor clp, ReplyAction ra, Change.Id id, ChangeInfo info) { @@ -60,9 +49,7 @@ if (messages != null) { for (MessageInfo msg : Natives.asList(messages)) { Message ui = new Message(this, msg); - if (loaded.contains(msg._revisionNumber())) { - ui.addComments(comments(msg)); - } + ui.addComments(comments(msg)); add(ui); } autoOpen(ChangeScreen.myLastReply(info)); @@ -99,18 +86,16 @@ replyAction.onReply(info); } - void addComments(int id, NativeMap<JsArray<CommentInfo>> map) { - loaded.add(id); - + void addComments(NativeMap<JsArray<CommentInfo>> map) { for (String path : map.keySet()) { for (CommentInfo c : Natives.asList(map.get(path))) { c.path(path); if (c.author() != null) { - AuthorRevision k = new AuthorRevision(c.author(), id); - List<CommentInfo> l = byAuthor.get(k); + int authorId = c.author()._accountId(); + List<CommentInfo> l = byAuthor.get(authorId); if (l == null) { l = new ArrayList<>(); - byAuthor.put(k, l); + byAuthor.put(authorId, l); } l.add(c); } @@ -118,58 +103,13 @@ } } - void load(int revisionNumber) { - if (revisionNumber > 0 && loaded.add(revisionNumber)) { - toLoad.add(revisionNumber); - start(); - } - } - - private void start() { - if (active >= 2 || toLoad.isEmpty() || !isAttached()) { - return; - } - - final int revisionNumber = toLoad.remove(0); - active++; - ChangeApi.revision(new PatchSet.Id(changeId, revisionNumber)) - .view("comments") - .get(new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() { - @Override - public void onSuccess(NativeMap<JsArray<CommentInfo>> result) { - addComments(revisionNumber, result); - update(revisionNumber); - --active; - start(); - } - - @Override - public void onFailure(Throwable caught) { - loaded.remove(revisionNumber); - loaded.removeAll(toLoad); - toLoad.clear(); - active--; - } - }); - } - - private void update(int revisionNumber) { - for (Widget child : getChildren()) { - Message ui = (Message) child; - MessageInfo info = ui.getMessageInfo(); - if (info._revisionNumber() == revisionNumber) { - ui.addComments(comments(info)); - } - } - } - private List<CommentInfo> comments(MessageInfo msg) { if (msg.author() == null) { return Collections.emptyList(); } - AuthorRevision k = new AuthorRevision(msg.author(), msg._revisionNumber()); - List<CommentInfo> list = byAuthor.get(k); + int authorId = msg.author()._accountId(); + List<CommentInfo> list = byAuthor.get(authorId); if (list == null) { return Collections.emptyList(); } @@ -187,34 +127,10 @@ if (match.isEmpty()) { return Collections.emptyList(); } else if (other.isEmpty()) { - byAuthor.remove(k); + byAuthor.remove(authorId); } else { - byAuthor.put(k, other); + byAuthor.put(authorId, other); } return match; } - - private static final class AuthorRevision { - final int author; - final int revision; - - AuthorRevision(AccountInfo author, int revision) { - this.author = author._account_id(); - this.revision = revision; - } - - @Override - public int hashCode() { - return author * 31 + revision; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof AuthorRevision)) { - return false; - } - AuthorRevision b = (AuthorRevision) o; - return author == b.author && revision == b.revision; - } - } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml index 59b05f0..e59420c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml
@@ -17,7 +17,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style type='com.google.gerrit.client.change.IncludedInBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.IncludedInBox.Style'> .includedInBox { min-width: 300px; max-width: 580px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java index a416894..f192a71 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
@@ -115,7 +115,8 @@ private Widget renderUsers(LabelInfo label) { Map<Integer, List<ApprovalInfo>> m = new HashMap<>(4); - int approved = 0, rejected = 0; + int approved = 0; + int rejected = 0; for (ApprovalInfo ai : Natives.asList(label.all())) { if (ai.value() != 0) { @@ -142,7 +143,7 @@ String val = LabelValue.formatValue(v.shortValue()); html.openSpan(); - html.setAttribute("title", label.value_text(val)); + html.setAttribute("title", label.valueText(val)); if (v.intValue() == approved) { html.setStyleName(style.label_ok()); } else if (v.intValue() == rejected) { @@ -171,12 +172,12 @@ private static boolean isApproved(LabelInfo label, ApprovalInfo ai) { return label.approved() != null - && label.approved()._account_id() == ai._account_id(); + && label.approved()._accountId() == ai._accountId(); } private static boolean isRejected(LabelInfo label, ApprovalInfo ai) { return label.rejected() != null - && label.rejected()._account_id() == ai._account_id(); + && label.rejected()._accountId() == ai._accountId(); } private String getStyleForLabel(LabelInfo label) { @@ -233,12 +234,12 @@ } else if (ai.email() != null) { name = ai.email(); } else { - name = Integer.toString(ai._account_id()); + name = Integer.toString(ai._accountId()); } String votableCategories = ""; if (votable != null) { - Set<String> s = votable.get(ai._account_id()).votableLabels(); + Set<String> s = votable.get(ai._accountId()).votableLabels(); if (!s.isEmpty()) { StringBuilder sb = new StringBuilder(Util.C.votable()); sb.append(" "); @@ -253,7 +254,7 @@ } html.openSpan() .setAttribute("role", "listitem") - .setAttribute(DATA_ID, ai._account_id()) + .setAttribute(DATA_ID, ai._accountId()) .setAttribute("title", getTitle(ai, votableCategories)) .setStyleName(style.label_user()); if (img != null) { @@ -269,7 +270,7 @@ html.closeSelf(); } html.append(name); - if (removable.contains(ai._account_id())) { + if (removable.contains(ai._accountId())) { html.openElement("button") .setAttribute("title", Util.M.removeReviewer(name)) .setAttribute("onclick", REMOVE + "(event)")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java index 8fa5a68..2d5dce0 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
@@ -33,15 +33,30 @@ interface Binder extends UiBinder<HTMLPanel, LineComment> {} private static final Binder uiBinder = GWT.create(Binder.class); + @UiField Element psLoc; + @UiField Element psNum; @UiField Element fileLoc; @UiField Element lineLoc; @UiField InlineHyperlink line; @UiField Element message; - LineComment(CommentLinkProcessor clp, PatchSet.Id ps, CommentInfo info) { + LineComment(CommentLinkProcessor clp, + PatchSet.Id defaultPs, + CommentInfo info) { initWidget(uiBinder.createAndBindUi(this)); - if (info.has_line()) { + PatchSet.Id ps; + if (info.patchSet() != defaultPs.get()) { + ps = new PatchSet.Id(defaultPs.getParentKey(), info.patchSet()); + psNum.setInnerText(Integer.toString(info.patchSet())); + } else { + ps = defaultPs; + psLoc.removeFromParent(); + psLoc = null; + psNum= null; + } + + if (info.hasLine()) { fileLoc.removeFromParent(); fileLoc = null;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml index 8dc1245..2890832 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:c='urn:import:com.google.gerrit.client.ui' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> .box { position: relative; } @@ -29,13 +29,16 @@ font-weight: bold; } .message { - margin-left: 111px; + margin-left: 135px; } </ui:style> <g:HTMLPanel styleName='{style.box}'> - <div class='{style.location}' ui:field='fileLoc'><ui:msg>File Comment</ui:msg></div> - <div class='{style.location}' ui:field='lineLoc'><ui:msg>Line <c:InlineHyperlink ui:field='line'/>:</ui:msg></div> + <div class='{style.location}'> + <span ui:field='psLoc'><ui:msg>PS<span ui:field='psNum'/>, </ui:msg></span> + <span ui:field='fileLoc'><ui:msg>File Comment</ui:msg></span> + <span ui:field='lineLoc'><ui:msg>Line <c:InlineHyperlink ui:field='line'/>:</ui:msg></span> + </div> <div class='{style.message}' ui:field='message'/> </g:HTMLPanel> </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java index 22d39a7..bc1ac30 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
@@ -121,13 +121,9 @@ } void setOpen(boolean open) { - if (open && info._revisionNumber() > 0) { - if (commentList == null) { - history.load(info._revisionNumber()); - } else if (!commentList.isEmpty()) { - renderComments(commentList); - commentList = Collections.emptyList(); - } + if (open && info._revisionNumber() > 0 && !commentList.isEmpty()) { + renderComments(commentList); + commentList = Collections.emptyList(); } setName(open); @@ -156,7 +152,6 @@ void autoOpen() { if (commentList == null) { autoOpen = true; - history.load(info._revisionNumber()); } else if (!commentList.isEmpty()) { setOpen(true); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml index 42cb39b..e362c07 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:c='urn:import:com.google.gerrit.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style type='com.google.gerrit.client.change.Message.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.Message.Style'> .messageBox { position: relative; width: 1168px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java index 665eff5..f3ab35e 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
@@ -128,7 +128,7 @@ @Override public void onSuccess(ChangeInfo result) { if (edit != null) { - edit.set_name(edit.commit().commit()); + edit.setName(edit.commit().commit()); result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit)); } render(result.revisions()); @@ -187,7 +187,7 @@ if (r.draft()) { sb.append(Resources.C.draft()).append(' '); } - if (r.has_draft_comments()) { + if (r.hasDraftComments()) { sb.openSpan() .addStyleName(style.draft_comment()) .setAttribute("title", Resources.C.draftCommentsTooltip())
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml index bd69cd6..7537aa4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style type='com.google.gerrit.client.change.PatchSetsBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.change.PatchSetsBox.Style'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; .revisionBox {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java index 6638dbe..105a3c8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
@@ -19,6 +19,7 @@ import com.google.gerrit.client.changes.ChangeInfo; import com.google.gerrit.client.changes.ChangeInfo.LabelInfo; import com.google.gerrit.client.changes.ReviewInput; +import com.google.gerrit.client.changes.ReviewInput.DraftHandling; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.Natives; import com.google.gerrit.common.PageLinks; @@ -41,12 +42,12 @@ } void set(ChangeInfo info, String commit, ReplyAction action) { - if (!info.has_permitted_labels() || !info.status().isOpen()) { + if (!info.hasPermittedLabels() || !info.status().isOpen()) { // Quick approve needs at least one label on an open change. setVisible(false); return; } - if (info.revision(commit).is_edit() || info.revision(commit).draft()) { + if (info.revision(commit).isEdit() || info.revision(commit).draft()) { setVisible(false); return; } @@ -55,52 +56,28 @@ String qValueStr = null; short qValue = 0; - for (LabelInfo label : Natives.asList(info.all_labels().values())) { - if (!info.permitted_labels().containsKey(label.name())) { - continue; - } - - JsArrayString values = info.permitted_values(label.name()); - if (values.length() == 0) { - continue; - } - - switch (label.status()) { - case NEED: // Label is required for submit. - break; - - case OK: // Label already applied. - case MAY: // Label is not required. - continue; - - case REJECT: // Submit cannot happen, do not quick approve. - case IMPOSSIBLE: - setVisible(false); - return; - } - + int index = info.getMissingLabelIndex(); + if (index != -1) { + LabelInfo label = Natives.asList(info.allLabels().values()).get(index); + JsArrayString values = info.permittedValues(label.name()); String s = values.get(values.length() - 1); short v = LabelInfo.parseValue(s); - if (v > 0 && s.equals(label.max_value())) { - if (qName != null) { - // Quick approve is available for one label only. - setVisible(false); - return; - } - + if (v > 0 && s.equals(label.maxValue())) { qName = label.name(); qValueStr = s; qValue = v; } } - if (qName != null) { - changeId = info.legacy_id(); + if (qName != null) { + changeId = info.legacyId(); revision = commit; input = ReviewInput.create(); + input.drafts(DraftHandling.PUBLISH_ALL_REVISIONS); input.label(qName, qValue); replyAction = action; setText(qName + qValueStr); + setVisible(true); } else { setVisible(false); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java index 0c78a67..0020c50 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
@@ -59,6 +59,7 @@ String pointer(); String row(); String subject(); + String submittable(); String tabPanel(); } @@ -171,6 +172,7 @@ getTab(Tab.CHERRY_PICKS).setShowBranches(true); getTab(Tab.SAME_TOPIC).setShowBranches(true); getTab(Tab.SAME_TOPIC).setShowProjects(true); + getTab(Tab.SAME_TOPIC).setShowSubmittable(true); } void set(final ChangeInfo info, final String revision) { @@ -178,7 +180,7 @@ setForOpenChange(info, revision); } - ChangeApi.revision(info.legacy_id().get(), revision).view("related") + ChangeApi.revision(info.legacyId().get(), revision).view("related") .get(new TabCallback<RelatedInfo>(Tab.RELATED_CHANGES, info.project(), revision) { @Override public JsArray<ChangeAndCommit> convert(RelatedInfo result) { @@ -188,8 +190,8 @@ StringBuilder cherryPicksQuery = new StringBuilder(); cherryPicksQuery.append(op("project", info.project())); - cherryPicksQuery.append(" ").append(op("change", info.change_id())); - cherryPicksQuery.append(" ").append(op("-change", info.legacy_id().get())); + cherryPicksQuery.append(" ").append(op("change", info.changeId())); + cherryPicksQuery.append(" ").append(op("-change", info.legacyId().get())); cherryPicksQuery.append(" -is:abandoned"); ChangeList.query(cherryPicksQuery.toString(), EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT), @@ -199,9 +201,11 @@ StringBuilder topicQuery = new StringBuilder(); topicQuery.append("status:open"); topicQuery.append(" ").append(op("topic", info.topic())); - topicQuery.append(" ").append(op("-change", info.legacy_id().get())); ChangeList.query(topicQuery.toString(), - EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT), + EnumSet.of(ListChangesOption.CURRENT_REVISION, + ListChangesOption.CURRENT_COMMIT, + ListChangesOption.DETAILED_LABELS, + ListChangesOption.LABELS), new TabChangeListCallback(Tab.SAME_TOPIC, info.project(), revision)); } } @@ -211,7 +215,7 @@ StringBuilder conflictsQuery = new StringBuilder(); conflictsQuery.append("status:open"); conflictsQuery.append(" is:mergeable"); - conflictsQuery.append(" ").append(op("conflicts", info.legacy_id().get())); + conflictsQuery.append(" ").append(op("conflicts", info.legacyId().get())); ChangeList.query(conflictsQuery.toString(), EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT), new TabChangeListCallback(Tab.CONFLICTING_CHANGES, info.project(), revision)); @@ -319,15 +323,16 @@ protected JsArray<ChangeAndCommit> convert(ChangeList l) { JsArray<ChangeAndCommit> arr = JavaScriptObject.createArray().cast(); for (ChangeInfo i : Natives.asList(l)) { - if (i.current_revision() != null && i.revisions().containsKey(i.current_revision())) { - RevisionInfo currentRevision = i.revision(i.current_revision()); + if (i.currentRevision() != null && i.revisions().containsKey(i.currentRevision())) { + RevisionInfo currentRevision = i.revision(i.currentRevision()); ChangeAndCommit c = ChangeAndCommit.create(); - c.set_id(i.id()); - c.set_commit(currentRevision.commit()); - c.set_change_number(i.legacy_id().get()); - c.set_revision_number(currentRevision._number()); - c.set_branch(i.branch()); - c.set_project(i.project()); + c.setId(i.id()); + c.setCommit(currentRevision.commit()); + c.setChangeNumber(i.legacyId().get()); + c.setRevisionNumber(currentRevision._number()); + c.setBranch(i.branch()); + c.setProject(i.project()); + c.setSubmittable(i.submittable() && i.mergeable()); arr.push(c); } } @@ -350,56 +355,60 @@ public final native CommitInfo commit() /*-{ return this.commit }-*/; final native String branch() /*-{ return this.branch }-*/; final native String project() /*-{ return this.project }-*/; + final native boolean submittable() /*-{ return this._submittable ? true : false; }-*/; - final native void set_id(String i) + final native void setId(String i) /*-{ if(i)this.change_id=i; }-*/; - final native void set_commit(CommitInfo c) + final native void setCommit(CommitInfo c) /*-{ if(c)this.commit=c; }-*/; - final native void set_branch(String b) + final native void setBranch(String b) /*-{ if(b)this.branch=b; }-*/; - final native void set_project(String b) + final native void setProject(String b) /*-{ if(b)this.project=b; }-*/; - public final Change.Id legacy_id() { - return has_change_number() ? new Change.Id(_change_number()) : null; + public final Change.Id legacyId() { + return hasChangeNumber() ? new Change.Id(_changeNumber()) : null; } - public final PatchSet.Id patch_set_id() { - return has_change_number() && has_revision_number() - ? new PatchSet.Id(legacy_id(), _revision_number()) + public final PatchSet.Id patchSetId() { + return hasChangeNumber() && hasRevisionNumber() + ? new PatchSet.Id(legacyId(), _revisionNumber()) : null; } - public final native boolean has_change_number() + public final native boolean hasChangeNumber() /*-{ return this.hasOwnProperty('_change_number') }-*/; - final native boolean has_revision_number() + final native boolean hasRevisionNumber() /*-{ return this.hasOwnProperty('_revision_number') }-*/; - final native boolean has_current_revision_number() + final native boolean hasCurrentRevisionNumber() /*-{ return this.hasOwnProperty('_current_revision_number') }-*/; - final native int _change_number() + final native int _changeNumber() /*-{ return this._change_number }-*/; - final native int _revision_number() + final native int _revisionNumber() /*-{ return this._revision_number }-*/; - final native int _current_revision_number() + final native int _currentRevisionNumber() /*-{ return this._current_revision_number }-*/; - final native void set_change_number(int n) + final native void setChangeNumber(int n) /*-{ this._change_number=n; }-*/; - final native void set_revision_number(int n) + final native void setRevisionNumber(int n) /*-{ this._revision_number=n; }-*/; - final native void set_current_revision_number(int n) + final native void setCurrentRevisionNumber(int n) /*-{ this._current_revision_number=n; }-*/; + final native void setSubmittable(boolean s) + /*-{ this._submittable=s; }-*/; + protected ChangeAndCommit() { } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java index 96d0d10..1a09fc5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
@@ -68,7 +68,8 @@ AbstractImagePrototype.create(Gerrit.RESOURCES.arrowRight()).getSafeHtml(); private static final native String init(String o) /*-{ - $wnd[o] = $entry(@com.google.gerrit.client.change.RelatedChangesTab::onOpen(Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/dom/client/Element;)); + $wnd[o] = $entry(@com.google.gerrit.client.change.RelatedChangesTab::onOpen( + Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/dom/client/Element;)); return o + '(event,this)'; }-*/; @@ -86,6 +87,7 @@ private boolean showBranches; private boolean showProjects; + private boolean showSubmittable; private boolean showIndirectAncestors; private boolean registerKeys; private int maxHeight; @@ -111,6 +113,10 @@ this.showProjects = showProjects; } + void setShowSubmittable(boolean submittable) { + this.showSubmittable = submittable; + } + void setShowIndirectAncestors(boolean showIndirectAncestors) { this.showIndirectAncestors = showIndirectAncestors; } @@ -296,19 +302,23 @@ sb.openSpan(); GitwebLink gw = Gerrit.getGitwebLink(); - if (gw != null && (!info.has_change_number() || !info.has_revision_number())) { + if (gw != null && (!info.hasChangeNumber() || !info.hasRevisionNumber())) { sb.setStyleName(RelatedChanges.R.css().gitweb()); sb.setAttribute("title", gw.getLinkName()); - sb.append('\u25CF'); + sb.append('\u25CF'); // Unicode 'BLACK CIRCLE' } else if (notConnected) { sb.setStyleName(RelatedChanges.R.css().indirect()); sb.setAttribute("title", Resources.C.indirectAncestor()); sb.append('~'); - } else if (info.has_current_revision_number() && info.has_revision_number() - && info._current_revision_number() != info._revision_number()) { + } else if (info.hasCurrentRevisionNumber() && info.hasRevisionNumber() + && info._currentRevisionNumber() != info._revisionNumber()) { sb.setStyleName(RelatedChanges.R.css().notCurrent()); sb.setAttribute("title", Util.C.notCurrent()); - sb.append('\u25CF'); + sb.append('\u25CF'); // Unicode 'BLACK CIRCLE' + } else if (showSubmittable && info.submittable()) { + sb.setStyleName(RelatedChanges.R.css().submittable()); + sb.setAttribute("title", Util.C.submittable()); + sb.append('\u2713'); // Unicode 'CHECK MARK' } else { sb.setStyleName(RelatedChanges.R.css().current()); } @@ -318,8 +328,8 @@ } private String url() { - if (info.has_change_number() && info.has_revision_number()) { - PatchSet.Id id = info.patch_set_id(); + if (info.hasChangeNumber() && info.hasRevisionNumber()) { + PatchSet.Id id = info.patchSetId(); return "#" + PageLinks.toChange( id.getParentKey(), id.getId());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java index 1f11e65..d4b6c42 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2015 The Android Open Source Project +// Copyright (C) 2015 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java index 77348f7..d6ab9ae 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2015 The Android Open Source Project +// Copyright (C) 2015 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.client.change;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml index 27849ee..17e8797 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml
@@ -20,7 +20,7 @@ xmlns:u='urn:import:com.google.gerrit.client.ui' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> .cancel { float: right; } </ui:style> <g:HTMLPanel>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java index 6e40979..cccab34 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
@@ -33,6 +33,7 @@ class ReplyAction { private final PatchSet.Id psId; private final String revision; + private final boolean hasDraftComments; private final ChangeScreen.Style style; private final CommentLinkProcessor clp; private final Widget replyButton; @@ -47,23 +48,25 @@ ReplyAction( ChangeInfo info, String revision, + boolean hasDraftComments, ChangeScreen.Style style, CommentLinkProcessor clp, Widget replyButton, Widget quickApproveButton) { this.psId = new PatchSet.Id( - info.legacy_id(), + info.legacyId(), info.revisions().get(revision)._number()); this.revision = revision; + this.hasDraftComments = hasDraftComments; this.style = style; this.clp = clp; this.replyButton = replyButton; this.quickApproveButton = quickApproveButton; - boolean current = revision.equals(info.current_revision()); - allLabels = info.all_labels(); - permittedLabels = current && info.has_permitted_labels() - ? info.permitted_labels() + boolean current = revision.equals(info.currentRevision()); + allLabels = info.allLabels(); + permittedLabels = current && info.hasPermittedLabels() + ? info.permittedLabels() : NativeMap.<JsArrayString> create(); } @@ -111,11 +114,15 @@ public void onClose(CloseEvent<PopupPanel> event) { if (popup == p) { popup = null; + if (hasDraftComments || replyBox.hasMessage()) { + replyButton.setStyleName(style.highlight()); + } } } }); p.add(replyBox); Window.scrollTo(0, 0); + replyButton.removeStyleName(style.highlight()); p.showRelativeTo(replyButton); GlobalKey.dialog(p); popup = p;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java index f9054fe..aec617a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -21,9 +21,9 @@ import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo; import com.google.gerrit.client.changes.ChangeInfo.LabelInfo; import com.google.gerrit.client.changes.ChangeInfo.MessageInfo; -import com.google.gerrit.client.changes.CommentApi; import com.google.gerrit.client.changes.CommentInfo; import com.google.gerrit.client.changes.ReviewInput; +import com.google.gerrit.client.changes.ReviewInput.DraftHandling; import com.google.gerrit.client.changes.Util; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.NativeMap; @@ -140,19 +140,19 @@ protected void onLoad() { commentsPanel.setVisible(false); post.setEnabled(false); - CommentApi.drafts(psId, new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() { - @Override - public void onSuccess(NativeMap<JsArray<CommentInfo>> result) { - attachComments(result); - displayComments(result); - post.setEnabled(true); - } + ChangeApi.drafts(psId.getParentKey().get()) + .get(new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() { + @Override + public void onSuccess(NativeMap<JsArray<CommentInfo>> result) { + displayComments(result); + post.setEnabled(true); + } - @Override - public void onFailure(Throwable caught) { - post.setEnabled(true); - } - }); + @Override + public void onFailure(Throwable caught) { + post.setEnabled(true); + } + }); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override @@ -180,8 +180,15 @@ postReview(); } + boolean hasMessage() { + return !message.getText().trim().isEmpty(); + } + private void postReview() { in.message(message.getText().trim()); + // Don't send any comments in the request; just publish everything, even if + // e.g. a draft was modified in another tab since we last looked it up. + in.drafts(DraftHandling.PUBLISH_ALL_REVISIONS); in.prePost(); ChangeApi.revision(psId.getParentKey().get(), revision) .view("review") @@ -284,7 +291,7 @@ List<LabelAndValues> checkboxes = new ArrayList<>(labels.size()); int row = 1; for (LabelAndValues lv : labels) { - if (isCheckBox(lv.info.value_set())) { + if (isCheckBox(lv.info.valueSet())) { checkboxes.add(lv); } else { renderRadio(row++, columns, lv); @@ -321,7 +328,7 @@ fmt.setStyleName(row, labelHelpColumn, style.label_help()); ApprovalInfo self = Gerrit.isSignedIn() - ? lv.info.for_user(Gerrit.getUserAccount().getId().get()) + ? lv.info.forUser(Gerrit.getUserAccount().getId().get()) : null; final LabelRadioGroup group = @@ -329,7 +336,7 @@ for (int i = 0; i < columns.size(); i++) { Short v = columns.get(i); if (lv.permitted.contains(v)) { - String text = lv.info.value_text(LabelValue.formatValue(v)); + String text = lv.info.valueText(LabelValue.formatValue(v)); LabelRadioButton b = new LabelRadioButton(group, text, v); if ((self != null && v == self.value()) || (self == null && v.equals(dv))) { b.setValue(true); @@ -345,7 +352,7 @@ private void renderCheckBox(int row, LabelAndValues lv) { ApprovalInfo self = Gerrit.isSignedIn() - ? lv.info.for_user(Gerrit.getUserAccount().getId().get()) + ? lv.info.forUser(Gerrit.getUserAccount().getId().get()) : null; final String id = lv.info.name(); @@ -366,7 +373,7 @@ CellFormatter fmt = labelsTable.getCellFormatter(); fmt.setStyleName(row, labelHelpColumn, style.label_help()); - labelsTable.setText(row, labelHelpColumn, lv.info.value_text("+1")); + labelsTable.setText(row, labelHelpColumn, lv.info.valueText("+1")); } private static boolean isCheckBox(Set<Short> values) { @@ -375,11 +382,6 @@ && values.contains((short) 1); } - private void attachComments(NativeMap<JsArray<CommentInfo>> result) { - in.drafts(ReviewInput.DraftHandling.KEEP); - in.comments(result); - } - private void displayComments(NativeMap<JsArray<CommentInfo>> m) { comments.clear();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml index a17d648..52f6b6a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style type='com.google.gerrit.client.change.ReplyBox.Styles'> + <ui:style gss='false' type='com.google.gerrit.client.change.ReplyBox.Styles'> .replyBox { } .label_name {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java index 47d6cad..fe0e6d0 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
@@ -48,7 +48,7 @@ public void onSuccess(ChangeInfo result) { sent = true; hide(); - Gerrit.display(PageLinks.toChange(result.legacy_id())); + Gerrit.display(PageLinks.toChange(result.legacyId())); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java index 18e5e87..ce35747 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
@@ -100,7 +100,7 @@ } void set(ChangeInfo info) { - this.changeId = info.legacy_id(); + this.changeId = info.legacyId(); display(info); reviewerSuggestOracle.setChange(changeId); openForm.setVisible(Gerrit.isSignedIn()); @@ -125,7 +125,7 @@ @UiHandler("addMe") void onAddMe(@SuppressWarnings("unused") ClickEvent e) { - String accountId = String.valueOf(Gerrit.getUserAccountInfo()._account_id()); + String accountId = String.valueOf(Gerrit.getUserAccountInfo()._accountId()); addReviewer(accountId, false); } @@ -198,22 +198,22 @@ private void display(ChangeInfo info) { Map<Integer, AccountInfo> r = new HashMap<>(); Map<Integer, AccountInfo> cc = new HashMap<>(); - for (LabelInfo label : Natives.asList(info.all_labels().values())) { + for (LabelInfo label : Natives.asList(info.allLabels().values())) { if (label.all() != null) { for (ApprovalInfo ai : Natives.asList(label.all())) { - (ai.value() != 0 ? r : cc).put(ai._account_id(), ai); + (ai.value() != 0 ? r : cc).put(ai._accountId(), ai); } } } for (Integer i : r.keySet()) { cc.remove(i); } - cc.remove(info.owner()._account_id()); + cc.remove(info.owner()._accountId()); Set<Integer> removable = new HashSet<>(); - if (info.removable_reviewers() != null) { - for (AccountInfo a : Natives.asList(info.removable_reviewers())) { - removable.add(a._account_id()); + if (info.removableReviewers() != null) { + for (AccountInfo a : Natives.asList(info.removableReviewers())) { + removable.add(a._accountId()); } } @@ -227,8 +227,8 @@ reviewersText.setInnerSafeHtml(rHtml); ccText.setInnerSafeHtml(ccHtml); if (Gerrit.isSignedIn()) { - int currentUser = Gerrit.getUserAccountInfo()._account_id(); - boolean showAddMeButton = info.owner()._account_id() != currentUser + int currentUser = Gerrit.getUserAccountInfo()._accountId(); + boolean showAddMeButton = info.owner()._accountId() != currentUser && !cc.containsKey(currentUser) && !r.containsKey(currentUser); addMe.setVisible(showAddMeButton); @@ -241,13 +241,13 @@ LabelInfo label = change.label(name); if (label.all() != null) { for (ApprovalInfo ai : Natives.asList(label.all())) { - int id = ai._account_id(); + int id = ai._accountId(); VotableInfo ad = d.get(id); if (ad == null) { ad = new VotableInfo(); d.put(id, ad); } - if (ai.has_value()) { + if (ai.hasValue()) { ad.votable(name); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml index 9924c1d..22e35e2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml
@@ -20,7 +20,7 @@ xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:u='urn:import:com.google.gerrit.client.ui'> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> button.openAdd { margin: 3px 3px 0 0; float: right;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java index 09d3476..f45b983 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
@@ -27,7 +27,7 @@ class SubmitAction { static void call(ChangeInfo changeInfo, RevisionInfo revisionInfo) { if (ChangeGlue.onSubmitChange(changeInfo, revisionInfo)) { - final Change.Id changeId = changeInfo.legacy_id(); + final Change.Id changeId = changeInfo.legacyId(); ChangeApi.submit( changeId.get(), revisionInfo.name(), new GerritCallback<SubmitInfo>() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java index 9f45678..b3eff93 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
@@ -68,12 +68,10 @@ } void set(ChangeInfo info, String revision) { - canEdit = info.has_actions() - && info.actions().containsKey("topic") - && info.actions().get("topic").enabled(); + canEdit = info.hasActions() && info.actions().containsKey("topic"); psId = new PatchSet.Id( - info.legacy_id(), + info.legacyId(), info.revisions().get(revision)._number()); initTopicLink(info);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml index ae46b10..e7e24b4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml
@@ -21,7 +21,7 @@ xmlns:x='urn:import:com.google.gerrit.client.ui'> <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/> <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> - <ui:style> + <ui:style gss='false'> .show { cursor: pointer; } .edit, .cancel { float: right; } </ui:style>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java index cdbc693..2daa9ea 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
@@ -48,7 +48,7 @@ HashSet<Integer> seen = new HashSet<>(); StringBuilder r = new StringBuilder(); for (MessageInfo m : newMessages) { - int a = m.author() != null ? m.author()._account_id() : 0; + int a = m.author() != null ? m.author()._accountId() : 0; if (seen.add(a)) { if (r.length() > 0) { r.append(", ");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml index 1c46b8c..1d5592b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:c='urn:import:com.google.gwtexpui.globalkey.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> .popup { position: fixed; bottom: 0;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css index 2803db3..f0101cb 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
@@ -13,14 +13,14 @@ * limitations under the License. */ -.pointer, .reviewed, .removeButton { +.pointer, .reviewed, .restoreDelete { padding: 0px; vertical-align: top; } .pointer { width: 12px; } -.reviewed, .removeButton { +.reviewed { height: 19px; width: 20px; } @@ -96,7 +96,11 @@ background-color: #d44; } -.removeButton button { +.restoreDelete div { + white-space: nowrap; +} + +.restoreDelete button { cursor: pointer; padding: 0; margin: 0 0 0 5px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css index 2e62b98..c0c828a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
@@ -68,7 +68,8 @@ .current, .gitweb, .indirect, -.notCurrent { +.notCurrent, +.submittable { display: inline-block; text-align: center; vertical-align: top; @@ -87,3 +88,8 @@ .notCurrent { color: #FFA62F; /* orange */ } + +.submittable { + color: #090; /* green */ + font-weight: bold; +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java index d9e9878..9974532 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -18,7 +18,9 @@ import com.google.gerrit.client.NotFoundScreen; import com.google.gerrit.client.rpc.Natives; import com.google.gerrit.client.rpc.ScreenLoadCallback; +import com.google.gerrit.client.ui.InlineHyperlink; import com.google.gerrit.client.ui.Screen; +import com.google.gerrit.common.PageLinks; import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.reviewdb.client.Account; import com.google.gwt.core.client.JsArray; @@ -28,8 +30,16 @@ import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; +import java.util.Set; public class AccountDashboardScreen extends Screen implements ChangeListScreen { + private static final Set<ListChangesOption> MY_DASHBOARD_OPTIONS; + static { + EnumSet<ListChangesOption> options = EnumSet.copyOf(ChangeTable.OPTIONS); + options.add(ListChangesOption.REVIEWED); + MY_DASHBOARD_OPTIONS = Collections.unmodifiableSet(options); + } + private final Account.Id ownerId; private final boolean mine; private ChangeTable table; @@ -61,10 +71,14 @@ incoming = new ChangeTable.Section(); closed = new ChangeTable.Section(); - outgoing.setTitleText(Util.C.outgoingReviews()); - incoming.setTitleText(Util.C.incomingReviews()); + String who = mine ? "self" : ownerId.toString(); + outgoing.setTitleWidget(new InlineHyperlink(Util.C.outgoingReviews(), + PageLinks.toChangeQuery(queryOutgoing(who)))); + incoming.setTitleWidget(new InlineHyperlink(Util.C.incomingReviews(), + PageLinks.toChangeQuery(queryIncoming(who)))); incoming.setHighlightUnreviewed(mine); - closed.setTitleText(Util.C.recentlyClosed()); + closed.setTitleWidget(new InlineHyperlink(Util.C.recentlyClosed(), + PageLinks.toChangeQuery(queryClosed(who)))); table.addSection(outgoing); table.addSection(incoming); @@ -73,24 +87,34 @@ table.setSavePointerId("owner:" + ownerId); } + private static String queryOutgoing(String who) { + return "is:open owner:" + who; + } + + private static String queryIncoming(String who) { + return "is:open reviewer:" + who + " -owner:" + who; + } + + private static String queryClosed(String who) { + return "is:closed (owner:" + who + " OR reviewer:" + who + ")"; + } + @Override protected void onLoad() { super.onLoad(); String who = mine ? "self" : ownerId.toString(); - ChangeList.query( + ChangeList.queryMultiple( new ScreenLoadCallback<JsArray<ChangeList>>(this) { @Override protected void preDisplay(JsArray<ChangeList> result) { display(result); } }, - mine - ? EnumSet.of(ListChangesOption.REVIEWED) - : EnumSet.noneOf(ListChangesOption.class), - "is:open owner:" + who, - "is:open reviewer:" + who + " -owner:" + who, - "is:closed (owner:" + who + " OR reviewer:" + who + ") -age:4w limit:10"); + mine ? MY_DASHBOARD_OPTIONS : DashboardTable.OPTIONS, + queryOutgoing(who), + queryIncoming(who), + queryClosed(who) + " -age:4w limit:10"); } @Override @@ -144,7 +168,9 @@ @Override public int compare(ChangeInfo a, ChangeInfo b) { int cmp = a.created().compareTo(b.created()); - if (cmp != 0) return cmp; + if (cmp != 0) { + return cmp; + } return a._number() - b._number(); } };
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java index 98595e7..7a5e239 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -51,7 +51,7 @@ input.project(emptyToNull(project)); input.branch(emptyToNull(branch)); input.subject(emptyToNull(subject)); - input.base_change(emptyToNull(base)); + input.baseChange(emptyToNull(base)); if (Gerrit.getConfig().isAllowDraftChanges()) { input.status(Change.Status.DRAFT.toString()); @@ -95,6 +95,21 @@ return call(id, "detail"); } + public static RestApi actions(int id, String revision) { + if (revision == null || revision.equals("")) { + revision = "current"; + } + return call(id, revision, "actions"); + } + + public static RestApi comments(int id) { + return call(id, "comments"); + } + + public static RestApi drafts(int id) { + return call(id, "drafts"); + } + public static void edit(int id, AsyncCallback<EditInfo> cb) { edit(id).get(cb); } @@ -172,7 +187,7 @@ /** Submit a specific revision of a change. */ public static void submit(int id, String commit, AsyncCallback<SubmitInfo> cb) { SubmitInput in = SubmitInput.create(); - in.wait_for_merge(true); + in.waitForMerge(true); call(id, commit, "submit").post(in, cb); } @@ -236,7 +251,7 @@ public final native void branch(String b) /*-{ if(b)this.branch=b; }-*/; public final native void project(String p) /*-{ if(p)this.project=p; }-*/; public final native void subject(String s) /*-{ if(s)this.subject=s; }-*/; - public final native void base_change(String b) /*-{ if(b)this.base_change=b; }-*/; + public final native void baseChange(String b) /*-{ if(b)this.base_change=b; }-*/; public final native void status(String s) /*-{ if(s)this.status=s; }-*/; protected CreateChangeInput() { @@ -266,7 +281,7 @@ } private static class SubmitInput extends JavaScriptObject { - final native void wait_for_merge(boolean b) /*-{ this.wait_for_merge=b; }-*/; + final native void waitForMerge(boolean b) /*-{ this.wait_for_merge=b; }-*/; static SubmitInput create() { return (SubmitInput) createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java index 6531129..efaa7c2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -22,6 +22,7 @@ String statusLongMerged(); String statusLongAbandoned(); String statusLongDraft(); + String submittable(); String readyToSubmit(); String mergeConflict(); String notCurrent();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties index 45415c5..40c6d24 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -3,6 +3,7 @@ statusLongMerged = Merged statusLongAbandoned = Abandoned statusLongDraft = Draft +submittable = Submittable readyToSubmit = Ready to Submit mergeConflict = Merge Conflict notCurrent = Not Current
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java index a00e329..0928cd8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
@@ -76,15 +76,15 @@ public static void rename(int id, String path, String newPath, AsyncCallback<VoidResult> cb) { Input in = Input.create(); - in.old_path(path); - in.new_path(newPath); + in.oldPath(path); + in.newPath(newPath); ChangeApi.edit(id).post(in, cb); } /** Restore (undo delete/modify) a file in the pending edit. */ public static void restore(int id, String path, AsyncCallback<VoidResult> cb) { Input in = Input.create(); - in.restore_path(path); + in.restorePath(path); ChangeApi.edit(id).post(in, cb); } @@ -101,9 +101,9 @@ return createObject().cast(); } - final native void restore_path(String p) /*-{ this.restore_path=p }-*/; - final native void old_path(String p) /*-{ this.old_path=p }-*/; - final native void new_path(String p) /*-{ this.new_path=p }-*/; + final native void restorePath(String p) /*-{ this.restore_path=p }-*/; + final native void oldPath(String p) /*-{ this.old_path=p }-*/; + final native void newPath(String p) /*-{ this.new_path=p }-*/; protected Input() { }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java index fff792f..9052fef 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
@@ -34,30 +34,31 @@ import java.sql.Timestamp; import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; public class ChangeInfo extends JavaScriptObject { public final void init() { - if (all_labels() != null) { - all_labels().copyKeysIntoChildren("_name"); + if (allLabels() != null) { + allLabels().copyKeysIntoChildren("_name"); } } - public final Project.NameKey project_name_key() { + public final Project.NameKey projectNameKey() { return new Project.NameKey(project()); } - public final Change.Id legacy_id() { + public final Change.Id legacyId() { return new Change.Id(_number()); } public final Timestamp created() { - Timestamp ts = _get_cts(); + Timestamp ts = _getCts(); if (ts == null) { ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(createdRaw()); - _set_cts(ts); + _setCts(ts); } return ts; } @@ -65,18 +66,18 @@ public final boolean hasEditBasedOnCurrentPatchSet() { JsArray<RevisionInfo> revList = revisions().values(); RevisionInfo.sortRevisionInfoByNumber(revList); - return revList.get(revList.length() - 1).is_edit(); + return revList.get(revList.length() - 1).isEdit(); } - private final native Timestamp _get_cts() /*-{ return this._cts; }-*/; - private final native void _set_cts(Timestamp ts) /*-{ this._cts = ts; }-*/; + private final native Timestamp _getCts() /*-{ return this._cts; }-*/; + private final native void _setCts(Timestamp ts) /*-{ this._cts = ts; }-*/; public final Timestamp updated() { return JavaSqlTimestamp_JsonSerializer.parseTimestamp(updatedRaw()); } - public final String id_abbreviated() { - return new Change.Key(change_id()).abbreviate(); + public final String idAbbreviated() { + return new Change.Key(changeId()).abbreviate(); } public final Change.Status status() { @@ -84,14 +85,14 @@ } public final Set<String> labels() { - return all_labels().keySet(); + return allLabels().keySet(); } public final native String id() /*-{ return this.id; }-*/; public final native String project() /*-{ return this.project; }-*/; public final native String branch() /*-{ return this.branch; }-*/; public final native String topic() /*-{ return this.topic; }-*/; - public final native String change_id() /*-{ return this.change_id; }-*/; + public final native String changeId() /*-{ return this.change_id; }-*/; public final native boolean mergeable() /*-{ return this.mergeable || false; }-*/; public final native int insertions() /*-{ return this.insertions; }-*/; public final native int deletions() /*-{ return this.deletions; }-*/; @@ -102,35 +103,92 @@ private final native String updatedRaw() /*-{ return this.updated; }-*/; public final native boolean starred() /*-{ return this.starred ? true : false; }-*/; public final native boolean reviewed() /*-{ return this.reviewed ? true : false; }-*/; - public final native NativeMap<LabelInfo> all_labels() /*-{ return this.labels; }-*/; + public final native NativeMap<LabelInfo> allLabels() /*-{ return this.labels; }-*/; public final native LabelInfo label(String n) /*-{ return this.labels[n]; }-*/; - public final native String current_revision() /*-{ return this.current_revision; }-*/; - public final native void set_current_revision(String r) /*-{ this.current_revision = r; }-*/; + public final native String currentRevision() /*-{ return this.current_revision; }-*/; + public final native void setCurrentRevision(String r) /*-{ this.current_revision = r; }-*/; + private final native void setSubmittable(boolean x) /*-{ this.submittable = x; }-*/; public final native NativeMap<RevisionInfo> revisions() /*-{ return this.revisions; }-*/; public final native RevisionInfo revision(String n) /*-{ return this.revisions[n]; }-*/; public final native JsArray<MessageInfo> messages() /*-{ return this.messages; }-*/; - public final native void set_edit(EditInfo edit) /*-{ this.edit = edit; }-*/; + public final native void setEdit(EditInfo edit) /*-{ this.edit = edit; }-*/; public final native EditInfo edit() /*-{ return this.edit; }-*/; - public final native boolean has_edit() /*-{ return this.hasOwnProperty('edit') }-*/; + public final native boolean hasEdit() /*-{ return this.hasOwnProperty('edit') }-*/; public final native JsArrayString hashtags() /*-{ return this.hashtags; }-*/; - public final native boolean has_permitted_labels() + public final native boolean hasPermittedLabels() /*-{ return this.hasOwnProperty('permitted_labels') }-*/; - public final native NativeMap<JsArrayString> permitted_labels() + public final native NativeMap<JsArrayString> permittedLabels() /*-{ return this.permitted_labels; }-*/; - public final native JsArrayString permitted_values(String n) + public final native JsArrayString permittedValues(String n) /*-{ return this.permitted_labels[n]; }-*/; - public final native JsArray<AccountInfo> removable_reviewers() + public final native JsArray<AccountInfo> removableReviewers() /*-{ return this.removable_reviewers; }-*/; - public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/; + public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/; public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/; final native int _number() /*-{ return this._number; }-*/; final native boolean _more_changes() /*-{ return this._more_changes ? true : false; }-*/; + public final boolean submittable() { + init(); + getMissingLabelIndex(); + return _submittable(); + } + + private final native boolean _submittable() + /*-{ return this.submittable ? true : false; }-*/; + + /** + * As a side effect this.submittable is evaluated and set accordingly. + * + * @return the index of the missing label or -1 + * if no label is missing, or if more than one label is missing. + */ + public final int getMissingLabelIndex() { + int i = -1; + int ret = -1; + List<LabelInfo> labels = Natives.asList(allLabels().values()); + for (LabelInfo label : labels) { + i++; + if (!permittedLabels().containsKey(label.name())) { + continue; + } + + JsArrayString values = permittedValues(label.name()); + if (values.length() == 0) { + continue; + } + + switch (label.status()) { + case NEED: // Label is required for submit. + if (ret != -1) { + // more than one label is missing, so it's unclear which to quick + // approve, return -1 + setSubmittable(false); + return -1; + } else { + ret = i; + } + continue; + + case OK: // Label already applied. + case MAY: // Label is not required. + continue; + + case REJECT: // Submit cannot happen, do not quick approve. + case IMPOSSIBLE: + setSubmittable(false); + return -1; + } + } + setSubmittable(ret == -1); + return ret; + } + protected ChangeInfo() { } @@ -155,10 +213,10 @@ public final native AccountInfo disliked() /*-{ return this.disliked; }-*/; public final native JsArray<ApprovalInfo> all() /*-{ return this.all; }-*/; - public final ApprovalInfo for_user(int user) { + public final ApprovalInfo forUser(int user) { JsArray<ApprovalInfo> all = all(); for (int i = 0; all != null && i < all.length(); i++) { - if (all.get(i)._account_id() == user) { + if (all.get(i)._accountId() == user) { return all.get(i); } } @@ -169,7 +227,7 @@ public final Set<String> values() { return Natives.keys(_values()); } - public final native String value_text(String n) /*-{ return this.values[n]; }-*/; + public final native String valueText(String n) /*-{ return this.values[n]; }-*/; public final native boolean optional() /*-{ return this.optional ? true : false; }-*/; public final native boolean blocking() /*-{ return this.blocking ? true : false; }-*/; @@ -182,11 +240,11 @@ return 0; }-*/; - public final String max_value() { - return LabelValue.formatValue(value_set().last()); + public final String maxValue() { + return LabelValue.formatValue(valueSet().last()); } - public final SortedSet<Short> value_set() { + public final SortedSet<Short> valueSet() { SortedSet<Short> values = new TreeSet<>(); for (String v : values()) { values.add(parseValue(v)); @@ -208,7 +266,7 @@ } public static class ApprovalInfo extends AccountInfo { - public final native boolean has_value() /*-{ return this.hasOwnProperty('value'); }-*/; + public final native boolean hasValue() /*-{ return this.hasOwnProperty('value'); }-*/; public final native short value() /*-{ return this.value || 0; }-*/; protected ApprovalInfo() { @@ -217,17 +275,17 @@ public static class EditInfo extends JavaScriptObject { public final native String name() /*-{ return this.name; }-*/; - public final native String set_name(String n) /*-{ this.name = n; }-*/; - public final native String base_revision() /*-{ return this.base_revision; }-*/; + public final native String setName(String n) /*-{ this.name = n; }-*/; + public final native String baseRevision() /*-{ return this.base_revision; }-*/; public final native CommitInfo commit() /*-{ return this.commit; }-*/; - public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/; + public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/; public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/; - public final native boolean has_fetch() /*-{ return this.hasOwnProperty('fetch') }-*/; + public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/; public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/; - public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/; + public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/; public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/; protected EditInfo() { @@ -249,19 +307,19 @@ public final native int _number() /*-{ return this._number; }-*/; public final native String name() /*-{ return this.name; }-*/; public final native boolean draft() /*-{ return this.draft || false; }-*/; - public final native boolean has_draft_comments() /*-{ return this.has_draft_comments || false; }-*/; - public final native boolean is_edit() /*-{ return this._number == 0; }-*/; + public final native boolean hasDraftComments() /*-{ return this.has_draft_comments || false; }-*/; + public final native boolean isEdit() /*-{ return this._number == 0; }-*/; public final native CommitInfo commit() /*-{ return this.commit; }-*/; - public final native void set_commit(CommitInfo c) /*-{ this.commit = c; }-*/; - public final native String edit_base() /*-{ return this.edit_base; }-*/; + public final native void setCommit(CommitInfo c) /*-{ this.commit = c; }-*/; + public final native String editBase() /*-{ return this.edit_base; }-*/; - public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/; + public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/; public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/; - public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/; + public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/; public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/; - public final native boolean has_fetch() /*-{ return this.hasOwnProperty('fetch') }-*/; + public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/; public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/; public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) { @@ -273,7 +331,7 @@ } private int num(RevisionInfo r) { - return !r.is_edit() ? 2 * (r._number() - 1) + 1 : 2 * editParent; + return !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent; } }); } @@ -282,8 +340,8 @@ for (int i = 0; i < list.length(); i++) { // edit under revisions? RevisionInfo editInfo = list.get(i); - if (editInfo.is_edit()) { - String parentRevision = editInfo.edit_base(); + if (editInfo.isEdit()) { + String parentRevision = editInfo.editBase(); // find parent for (int j = 0; j < list.length(); j++) { RevisionInfo parentInfo = list.get(j); @@ -323,7 +381,7 @@ public final native GitPerson committer() /*-{ return this.committer; }-*/; public final native String subject() /*-{ return this.subject; }-*/; public final native String message() /*-{ return this.message; }-*/; - public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; protected CommitInfo() { } @@ -357,7 +415,7 @@ } public static class MergeableInfo extends JavaScriptObject { - public final native String submit_type() /*-{ return this.submit_type }-*/; + public final native String submitType() /*-{ return this.submit_type }-*/; public final native boolean mergeable() /*-{ return this.mergeable }-*/; protected MergeableInfo() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java index b1866dd..5fcaf24 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
@@ -20,52 +20,68 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtorm.client.KeyUtil; -import java.util.EnumSet; +import java.util.Set; /** List of changes available from {@code /changes/}. */ public class ChangeList extends JsArray<ChangeInfo> { private static final String URI = "/changes/"; - private static final EnumSet<ListChangesOption> OPTIONS = EnumSet.of( - ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS); - /** Run 2 or more queries in a single remote invocation. */ - public static void query( - AsyncCallback<JsArray<ChangeList>> callback, - EnumSet<ListChangesOption> options, + /** Run multiple queries in a single remote invocation. */ + public static void queryMultiple( + final AsyncCallback<JsArray<ChangeList>> callback, + Set<ListChangesOption> options, String... queries) { - assert queries.length >= 2; // At least 2 is required for correct result. + if (queries.length == 0) { + return; + } RestApi call = new RestApi(URI); for (String q : queries) { call.addParameterRaw("q", KeyUtil.encode(q)); } - OPTIONS.addAll(options); - addOptions(call, OPTIONS); - call.get(callback); + addOptions(call, options); + if (queries.length == 1) { + // Server unwraps a single query, so wrap it back in an array for the + // callback. + call.get(new AsyncCallback<ChangeList>() { + @Override + public void onSuccess(ChangeList result) { + JsArray<ChangeList> wrapped = JsArray.createArray(1).cast(); + wrapped.set(0, result); + callback.onSuccess(wrapped); + } + + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + }); + } else { + call.get(callback); + } } public static void query(String query, - EnumSet<ListChangesOption> options, + Set<ListChangesOption> options, AsyncCallback<ChangeList> callback) { - RestApi call = newQuery(query); - addOptions(call, options); - call.get(callback); + query(query, options, callback, 0, 0); } - public static void next(String query, - int start, int limit, - AsyncCallback<ChangeList> callback) { + public static void query(String query, + Set<ListChangesOption> options, + AsyncCallback<ChangeList> callback, + int start, int limit) { RestApi call = newQuery(query); if (limit > 0) { call.addParameter("n", limit); } - addOptions(call, OPTIONS); + addOptions(call, options); if (start != 0) { call.addParameter("S", start); } call.get(callback); } - public static void addOptions(RestApi call, EnumSet<ListChangesOption> s) { + public static void addOptions(RestApi call, Set<ListChangesOption> s) { call.addParameterRaw("O", Integer.toHexString(ListChangesOption.toBits(s))); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java index 8b71448..c52ac32 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -27,6 +27,7 @@ import com.google.gerrit.client.ui.NeedsSignInKeyCommand; import com.google.gerrit.client.ui.ProjectLink; import com.google.gerrit.common.PageLinks; +import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.ReviewCategoryStrategy; import com.google.gerrit.reviewdb.client.Change; import com.google.gwt.dom.client.Element; @@ -45,9 +46,17 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; public class ChangeTable extends NavigationTable<ChangeInfo> { + // If changing default options, also update in + // ChangeIT#defaultSearchDoesNotTouchDatabase(). + static final Set<ListChangesOption> OPTIONS = + Collections.unmodifiableSet(EnumSet.of( + ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS)); + private static final int C_STAR = 1; private static final int C_ID = 2; private static final int C_SUBJECT = 3; @@ -118,13 +127,13 @@ @Override protected Object getRowItemKey(final ChangeInfo item) { - return item.legacy_id(); + return item.legacyId(); } @Override protected void onOpenRow(final int row) { final ChangeInfo c = getRowItem(row); - final Change.Id id = c.legacy_id(); + final Change.Id id = c.legacyId(); Gerrit.display(PageLinks.toChange(id)); } @@ -208,10 +217,10 @@ CellFormatter fmt = table.getCellFormatter(); if (Gerrit.isSignedIn()) { table.setWidget(row, C_STAR, StarredChanges.createIcon( - c.legacy_id(), + c.legacyId(), c.starred())); } - table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacy_id()), c)); + table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacyId()), c)); String subject = Util.cropSubject(c.subject()); table.setWidget(row, C_SUBJECT, new TableChangeLink(subject, c)); @@ -229,8 +238,8 @@ table.setText(row, C_OWNER, ""); } - table.setWidget(row, C_PROJECT, new ProjectLink(c.project_name_key())); - table.setWidget(row, C_BRANCH, new BranchLink(c.project_name_key(), c + table.setWidget(row, C_PROJECT, new ProjectLink(c.projectNameKey())); + table.setWidget(row, C_BRANCH, new BranchLink(c.projectNameKey(), c .status(), c.branch(), c.topic())); if (Gerrit.isSignedIn() && Gerrit.getUserAccount().getGeneralPreferences() @@ -447,7 +456,7 @@ private final class TableChangeLink extends ChangeLink { private TableChangeLink(final String text, final ChangeInfo c) { - super(text, c.legacy_id()); + super(text, c.legacyId()); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java index 52e5d40..c69ee57 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
@@ -29,7 +29,7 @@ n.path(path); n.side(side); if (range != null) { - n.line(range.end_line()); + n.line(range.endLine()); n.range(range); } else if (line > 0) { n.line(line); @@ -41,11 +41,11 @@ CommentInfo n = createObject().cast(); n.path(r.path()); n.side(r.side()); - n.in_reply_to(r.id()); - if (r.has_range()) { - n.line(r.range().end_line()); + n.inReplyTo(r.id()); + if (r.hasRange()) { + n.line(r.range().endLine()); n.range(r.range()); - } else if (r.has_line()) { + } else if (r.hasLine()) { n.line(r.line()); } return n; @@ -56,12 +56,12 @@ n.path(s.path()); n.side(s.side()); n.id(s.id()); - n.in_reply_to(s.in_reply_to()); + n.inReplyTo(s.inReplyTo()); n.message(s.message()); - if (s.has_range()) { - n.line(s.range().end_line()); + if (s.hasRange()) { + n.line(s.range().endLine()); n.range(s.range()); - } else if (s.has_line()) { + } else if (s.hasLine()) { n.line(s.line()); } return n; @@ -71,7 +71,7 @@ public final native void id(String i) /*-{ this.id = i }-*/; public final native void line(int n) /*-{ this.line = n }-*/; public final native void range(CommentRange r) /*-{ this.range = r }-*/; - public final native void in_reply_to(String i) /*-{ this.in_reply_to = i }-*/; + public final native void inReplyTo(String i) /*-{ this.in_reply_to = i }-*/; public final native void message(String m) /*-{ this.message = m }-*/; public final void side(Side side) { @@ -81,7 +81,8 @@ public final native String path() /*-{ return this.path }-*/; public final native String id() /*-{ return this.id }-*/; - public final native String in_reply_to() /*-{ return this.in_reply_to }-*/; + public final native String inReplyTo() /*-{ return this.in_reply_to }-*/; + public final native int patchSet() /*-{ return this.patch_set }-*/; public final Side side() { String s = sideRaw(); @@ -108,8 +109,8 @@ public final native AccountInfo author() /*-{ return this.author }-*/; public final native int line() /*-{ return this.line || 0 }-*/; - public final native boolean has_line() /*-{ return this.hasOwnProperty('line') }-*/; - public final native boolean has_range() /*-{ return this.hasOwnProperty('range') }-*/; + public final native boolean hasLine() /*-{ return this.hasOwnProperty('line') }-*/; + public final native boolean hasRange() /*-{ return this.hasOwnProperty('range') }-*/; public final native CommentRange range() /*-{ return this.range }-*/; public final native String message() /*-{ return this.message }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java index 9e97c56..26e11ae 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
@@ -20,14 +20,12 @@ import com.google.gerrit.client.ui.InlineHyperlink; import com.google.gerrit.client.ui.Screen; import com.google.gerrit.common.PageLinks; -import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gwt.core.client.JsArray; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.http.client.URL; import com.google.gwtexpui.globalkey.client.KeyCommand; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; import java.util.ListIterator; @@ -104,33 +102,19 @@ @Override protected void onLoad() { super.onLoad(); - - if (queries.size() == 1) { - ChangeList.next(queries.get(0), - 0, 0, - new GerritCallback<ChangeList>() { - @Override - public void onSuccess(ChangeList result) { - updateColumnsForLabels(result); - sections.get(0).display(result); - finishDisplay(); + ChangeList.queryMultiple( + new GerritCallback<JsArray<ChangeList>>() { + @Override + public void onSuccess(JsArray<ChangeList> result) { + List<ChangeList> cls = Natives.asList(result); + updateColumnsForLabels(cls.toArray(new ChangeList[cls.size()])); + for (int i = 0; i < cls.size(); i++) { + sections.get(i).display(cls.get(i)); } - }); - } else if (! queries.isEmpty()) { - ChangeList.query( - new GerritCallback<JsArray<ChangeList>>() { - @Override - public void onSuccess(JsArray<ChangeList> result) { - List<ChangeList> cls = Natives.asList(result); - updateColumnsForLabels(cls.toArray(new ChangeList[cls.size()])); - for (int i = 0; i < cls.size(); i++) { - sections.get(i).display(cls.get(i)); - } - finishDisplay(); - } - }, - EnumSet.noneOf(ListChangesOption.class), - queries.toArray(new String[queries.size()])); - } + finishDisplay(); + } + }, + OPTIONS, + queries.toArray(new String[queries.size()])); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java index 488b34b..dddbd61 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
@@ -54,7 +54,7 @@ if (isAttached()) { if (result.length() == 1 && isSingleQuery(query)) { ChangeInfo c = result.get(0); - Change.Id id = c.legacy_id(); + Change.Id id = c.legacyId(); Gerrit.display(PageLinks.toChange(id)); } else { display(result); @@ -74,7 +74,8 @@ @Override protected void onLoad() { super.onLoad(); - ChangeList.next(query, start, pageSize, loadCallback()); + ChangeList.query( + query, ChangeTable.OPTIONS, loadCallback(), start, pageSize); } private static boolean isSingleQuery(String query) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java index 7651495..096dbd0 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
@@ -24,7 +24,7 @@ } public static enum DraftHandling { - DELETE, PUBLISH, KEEP + DELETE, PUBLISH, KEEP, PUBLISH_ALL_REVISIONS } public static ReviewInput create() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java new file mode 100644 index 0000000..5e66d83 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java
@@ -0,0 +1,82 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.config; + +import com.google.gerrit.client.rpc.Natives; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AuthType; +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArray; + +import java.util.ArrayList; +import java.util.List; + +public class AuthInfo extends JavaScriptObject { + public final AuthType authType() { + return AuthType.valueOf(authTypeRaw()); + } + + public final boolean isOpenId() { + return authType() == AuthType.OPENID; + } + + public final boolean isOAuth() { + return authType() == AuthType.OAUTH; + } + + public final boolean isDev() { + return authType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT; + } + + public final boolean isClientSslCertLdap() { + return authType() == AuthType.CLIENT_SSL_CERT_LDAP; + } + + public final boolean isCustomExtension() { + return authType() == AuthType.CUSTOM_EXTENSION; + } + + public final boolean canEdit(Account.FieldName f) { + return editableAccountFields().contains(f); + } + + public final List<Account.FieldName> editableAccountFields() { + List<Account.FieldName> fields = new ArrayList<>(); + for (AccountFieldNameInfo f : Natives.asList(_editableAccountFields())) { + fields.add(f.get()); + } + return fields; + } + + public final native boolean useContributorAgreements() + /*-{ return this.use_contributor_agreements || false; }-*/; + private final native String authTypeRaw() /*-{ return this.auth_type; }-*/; + private final native JsArray<AccountFieldNameInfo> _editableAccountFields() + /*-{ return this.editable_account_fields; }-*/; + + protected AuthInfo() { + } + + private static class AccountFieldNameInfo extends JavaScriptObject { + final Account.FieldName get() { + return Account.FieldName.valueOf(getRaw()); + } + + private final native String getRaw() /*-{ return this; }-*/; + + protected AccountFieldNameInfo() { + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java index 5dedaf0..5d79390 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
@@ -37,4 +37,8 @@ public static void defaultPreferences(AsyncCallback<Preferences> cb) { new RestApi("/config/server/preferences").get(cb); } + + public static void serverInfo(AsyncCallback<ServerInfo> cb) { + new RestApi("/config/server/info").get(cb); + } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java new file mode 100644 index 0000000..e97d472 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java
@@ -0,0 +1,85 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.config; + +import com.google.gerrit.client.rpc.NativeMap; +import com.google.gerrit.client.rpc.NativeString; +import com.google.gerrit.client.rpc.Natives; +import com.google.gwt.core.client.JavaScriptObject; + +import java.util.HashSet; +import java.util.Set; + +public class DownloadInfo extends JavaScriptObject { + public final Set<String> schemes() { + return Natives.keys(_schemes()); + } + public final native DownloadSchemeInfo scheme(String n) /*-{ return this.schemes[n]; }-*/; + private final native NativeMap<DownloadSchemeInfo> _schemes() /*-{ return this.schemes; }-*/; + + protected DownloadInfo() { + } + + public static class DownloadSchemeInfo extends JavaScriptObject { + public final Set<String> commandNames() { + return Natives.keys(_commands()); + } + + public final Set<DownloadCommandInfo> commands(String project) { + Set<DownloadCommandInfo> commands = new HashSet<>(); + for (String commandName : commandNames()) { + commands.add(new DownloadCommandInfo(commandName, command(commandName, + project))); + } + return commands; + } + + public final String command(String commandName, String project) { + return command(commandName).replaceAll("\\$\\{project\\}", project); + } + + public final String getUrl(String project) { + return url().replaceAll("\\$\\{project\\}", project); + } + + public final native String name() /*-{ return this.name; }-*/; + public final native String url() /*-{ return this.url; }-*/; + public final native boolean isAuthRequired() /*-{ return this.is_auth_required || false; }-*/; + public final native boolean isAuthSupported() /*-{ return this.is_auth_supported || false; }-*/; + public final native String command(String n) /*-{ return this.commands[n]; }-*/; + private final native NativeMap<NativeString> _commands() /*-{ return this.commands; }-*/; + + protected DownloadSchemeInfo() { + } + } + + public static class DownloadCommandInfo { + private final String name; + private final String command; + + DownloadCommandInfo(String name, String command) { + this.name = name; + this.command = command; + } + + public String name() { + return name; + } + + public String command() { + return command; + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java new file mode 100644 index 0000000..33036ad --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.config; + +import com.google.gerrit.reviewdb.client.Project; +import com.google.gwt.core.client.JavaScriptObject; + +public class GerritInfo extends JavaScriptObject { + public final Project.NameKey allProjectsNameKey() { + return new Project.NameKey(allProjects()); + } + + public final boolean isAllProjects(Project.NameKey p) { + return allProjectsNameKey().equals(p); + } + + public final Project.NameKey allUsersNameKey() { + return new Project.NameKey(allUsers()); + } + + public final boolean isAllUsers(Project.NameKey p) { + return allUsersNameKey().equals(p); + } + + public final native String allProjects() /*-{ return this.all_projects; }-*/; + public final native String allUsers() /*-{ return this.all_users; }-*/; + + protected GerritInfo() { + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java new file mode 100644 index 0000000..a1e8dbc --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java
@@ -0,0 +1,38 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.config; + +import com.google.gwt.core.client.JavaScriptObject; + +public class ServerInfo extends JavaScriptObject { + public final native AuthInfo auth() /*-{ return this.auth; }-*/; + public final native ContactStoreInfo contactStore() /*-{ return this.contact_store; }-*/; + public final native DownloadInfo download() /*-{ return this.download; }-*/; + public final native GerritInfo gerrit() /*-{ return this.gerrit; }-*/; + + public final boolean hasContactStore() { + return contactStore() != null; + } + + protected ServerInfo() { + } + + public static class ContactStoreInfo extends JavaScriptObject { + public final native String url() /*-{ return this.url; }-*/; + + protected ContactStoreInfo() { + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java index fed8f91..8ff11e8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
@@ -124,7 +124,7 @@ padding = new ArrayList<>(); paddingDivs = new ArrayList<>(); - String diffColor = diff.meta_a() == null || diff.meta_b() == null + String diffColor = diff.metaA() == null || diff.metaB() == null ? DiffTable.style.intralineBg() : DiffTable.style.diff(); @@ -175,8 +175,8 @@ colorLines(cmA, color, startA, aLen); colorLines(cmB, color, startB, bLen); - markEdit(cmA, startA, a, region.edit_a()); - markEdit(cmB, startB, b, region.edit_b()); + markEdit(cmA, startA, a, region.editA()); + markEdit(cmB, startB, b, region.editB()); addPadding(cmA, startA + aLen - 1, bLen - aLen); addPadding(cmB, startB + bLen - 1, aLen - bLen); addGutterTag(region, startA, startB);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java index ca56bf4..0e85a2f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java index 514a3be..4e1a3e1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
@@ -218,8 +218,8 @@ info, expandAll); - if (info.in_reply_to() != null) { - PublishedBox r = published.get(info.in_reply_to()); + if (info.inReplyTo() != null) { + PublishedBox r = published.get(info.inReplyTo()); if (r != null) { r.setReplyBox(box); } @@ -391,7 +391,8 @@ return w; } - int lineA, lineB; + int lineA; + int lineB; if (line == 0) { lineA = lineB = 0; } else if (side == DisplaySide.A) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java index a38a6ca..9f46e9b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
@@ -38,10 +38,10 @@ to.line() + 1, to.ch()); } - public final native int start_line() /*-{ return this.start_line; }-*/; - public final native int start_character() /*-{ return this.start_character; }-*/; - public final native int end_line() /*-{ return this.end_line; }-*/; - public final native int end_character() /*-{ return this.end_character; }-*/; + public final native int startLine() /*-{ return this.start_line; }-*/; + public final native int startCharacter() /*-{ return this.start_character; }-*/; + public final native int endLine() /*-{ return this.end_line; }-*/; + public final native int endCharacter() /*-{ return this.end_character; }-*/; private final native void set(int sl, int sc, int el, int ec) /*-{ this.start_line = sl;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java index 9b3ac38..1e5c5e5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java index 7140e07..f7f4528 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
@@ -30,24 +30,24 @@ public static final String GITLINK = "x-git/gitlink"; public static final String SYMLINK = "x-git/symlink"; - public final native FileMeta meta_a() /*-{ return this.meta_a; }-*/; - public final native FileMeta meta_b() /*-{ return this.meta_b; }-*/; - public final native JsArrayString diff_header() /*-{ return this.diff_header; }-*/; + public final native FileMeta metaA() /*-{ return this.meta_a; }-*/; + public final native FileMeta metaB() /*-{ return this.meta_b; }-*/; + public final native JsArrayString diffHeader() /*-{ return this.diff_header; }-*/; public final native JsArray<Region> content() /*-{ return this.content; }-*/; - public final native JsArray<DiffWebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; public final native boolean binary() /*-{ return this.binary || false; }-*/; - public final List<WebLinkInfo> side_by_side_web_links() { + public final List<WebLinkInfo> sideBySideWebLinks() { return filterWebLinks(DiffView.SIDE_BY_SIDE); } - public final List<WebLinkInfo> unified_web_links() { + public final List<WebLinkInfo> unifiedWebLinks() { return filterWebLinks(DiffView.UNIFIED_DIFF); } private final List<WebLinkInfo> filterWebLinks(DiffView diffView) { List<WebLinkInfo> filteredDiffWebLinks = new LinkedList<>(); - List<DiffWebLinkInfo> allDiffWebLinks = Natives.asList(web_links()); + List<DiffWebLinkInfo> allDiffWebLinks = Natives.asList(webLinks()); if (allDiffWebLinks != null) { for (DiffWebLinkInfo webLink : allDiffWebLinks) { if (diffView == DiffView.SIDE_BY_SIDE @@ -63,22 +63,22 @@ return filteredDiffWebLinks; } - public final ChangeType change_type() { - return ChangeType.valueOf(change_typeRaw()); + public final ChangeType changeType() { + return ChangeType.valueOf(changeTypeRaw()); } - private final native String change_typeRaw() + private final native String changeTypeRaw() /*-{ return this.change_type }-*/; - public final IntraLineStatus intraline_status() { - String s = intraline_statusRaw(); + public final IntraLineStatus intralineStatus() { + String s = intralineStatusRaw(); return s != null ? IntraLineStatus.valueOf(s) : IntraLineStatus.OFF; } - private final native String intraline_statusRaw() + private final native String intralineStatusRaw() /*-{ return this.intraline_status }-*/; - public final boolean has_skip() { + public final boolean hasSkip() { JsArray<Region> c = content(); for (int i = 0; i < c.length(); i++) { if (c.get(i).skip() != 0) { @@ -88,7 +88,7 @@ return false; } - public final String text_a() { + public final String textA() { StringBuilder s = new StringBuilder(); JsArray<Region> c = content(); for (int i = 0; i < c.length(); i++) { @@ -103,7 +103,7 @@ return s.toString(); } - public final String text_b() { + public final String textB() { StringBuilder s = new StringBuilder(); JsArray<Region> c = content(); for (int i = 0; i < c.length(); i++) { @@ -133,9 +133,9 @@ public static class FileMeta extends JavaScriptObject { public final native String name() /*-{ return this.name; }-*/; - public final native String content_type() /*-{ return this.content_type; }-*/; + public final native String contentType() /*-{ return this.content_type; }-*/; public final native int lines() /*-{ return this.lines || 0 }-*/; - public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; protected FileMeta() { } @@ -148,8 +148,8 @@ public final native int skip() /*-{ return this.skip || 0; }-*/; public final native boolean common() /*-{ return this.common || false; }-*/; - public final native JsArray<Span> edit_a() /*-{ return this.edit_a }-*/; - public final native JsArray<Span> edit_b() /*-{ return this.edit_b }-*/; + public final native JsArray<Span> editA() /*-{ return this.edit_a }-*/; + public final native JsArray<Span> editB() /*-{ return this.edit_b }-*/; protected Region() { }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java index 1105476..b188c59 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -151,13 +151,13 @@ void set(DiffPreferences prefs, JsArray<RevisionInfo> list, DiffInfo info, boolean editExists, int currentPatchSet, boolean open, boolean binary) { - this.changeType = info.change_type(); - patchSetSelectBoxA.setUpPatchSetNav(list, info.meta_a(), editExists, + this.changeType = info.changeType(); + patchSetSelectBoxA.setUpPatchSetNav(list, info.metaA(), editExists, currentPatchSet, open, binary); - patchSetSelectBoxB.setUpPatchSetNav(list, info.meta_b(), editExists, + patchSetSelectBoxB.setUpPatchSetNav(list, info.metaB(), editExists, currentPatchSet, open, binary); - JsArrayString hdr = info.diff_header(); + JsArrayString hdr = info.diffHeader(); if (hdr != null) { StringBuilder b = new StringBuilder(); for (int i = 1; i < hdr.length(); i++) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml index 504d9c0..99ecc9b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -17,7 +17,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:d='urn:import:com.google.gerrit.client.diff'> - <ui:style type='com.google.gerrit.client.diff.DiffTable.DiffTableStyle'> + <ui:style gss='false' type='com.google.gerrit.client.diff.DiffTable.DiffTableStyle'> @external .CodeMirror, .CodeMirror-selectedtext; @external .CodeMirror-linenumber; @external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java index 5bf56c2..21b2f50 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -244,7 +244,7 @@ } private void restoreSelection() { - if (getFromTo() != null && comment.in_reply_to() == null) { + if (getFromTo() != null && comment.inReplyTo() == null) { getCm().setSelection(getFromTo().from(), getFromTo().to()); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml index 0c97e8b..a363c06 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml
@@ -20,7 +20,7 @@ xmlns:e='urn:import:com.google.gwtexpui.globalkey.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/> - <ui:style> + <ui:style gss='false'> .draft { width: 45px; text-align: center;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java index c4459b6..77b28d4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
@@ -25,9 +25,9 @@ public class FileInfo extends JavaScriptObject { public final native String path() /*-{ return this.path; }-*/; - public final native String old_path() /*-{ return this.old_path; }-*/; - public final native int lines_inserted() /*-{ return this.lines_inserted || 0; }-*/; - public final native int lines_deleted() /*-{ return this.lines_deleted || 0; }-*/; + public final native String oldPath() /*-{ return this.old_path; }-*/; + public final native int linesInserted() /*-{ return this.lines_inserted || 0; }-*/; + public final native int linesDeleted() /*-{ return this.lines_deleted || 0; }-*/; public final native boolean binary() /*-{ return this.binary || false; }-*/; public final native String status() /*-{ return this.status; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java index 2c551c0..42ae61d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
@@ -67,7 +67,7 @@ } private static enum ReviewedState { - AUTO_REVIEW, LOADED; + AUTO_REVIEW, LOADED } @UiField CheckBox reviewed;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml index c58eef7..f13c9a3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
@@ -20,7 +20,7 @@ xmlns:x='urn:import:com.google.gerrit.client.ui'> <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/> <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/> - <ui:style> + <ui:style gss='false'> .header { position: relative; height: 16px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java index 7287a65..7c8bc21 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2014 The Android Open Source Project +// Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml index d7c8fc9..6a18c4d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml
@@ -18,7 +18,7 @@ xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.GerritResources'/> - <ui:style> + <ui:style gss='false'> .bubble { z-index: 150; white-space: nowrap;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java index aed2218..e597398 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ linkPanel.add(createEditIcon()); } } - List<WebLinkInfo> webLinks = Natives.asList(meta.web_links()); + List<WebLinkInfo> webLinks = Natives.asList(meta.webLinks()); if (webLinks != null) { for (WebLinkInfo webLink : webLinks) { linkPanel.add(webLink.toAnchor());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml index cc8dd74..6e526ec 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml
@@ -20,7 +20,7 @@ <ui:with field='res' type='com.google.gerrit.client.GerritResources'/> <ui:with field='patchConstants' type='com.google.gerrit.client.patches.PatchConstants'/> - <ui:style type='com.google.gerrit.client.diff.PatchSetSelectBox.BoxStyle'> + <ui:style gss='false' type='com.google.gerrit.client.diff.PatchSetSelectBox.BoxStyle'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor; .table {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml index 62d2eac..e011091 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
@@ -17,7 +17,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:x='urn:import:com.google.gerrit.client.ui'> - <ui:style type='com.google.gerrit.client.diff.PreferencesBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.diff.PreferencesBox.Style'> @external .gwt-TextBox; @external .gwt-ToggleButton .html-face; @external .gwt-ToggleButton-up;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java index 7d74c2b..a32d7b8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml index bcc34e2..46b76ca 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
@@ -19,7 +19,7 @@ xmlns:c='urn:import:com.google.gerrit.client' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/> - <ui:style type='com.google.gerrit.client.diff.PublishedBox.Style'> + <ui:style gss='false' type='com.google.gerrit.client.diff.PublishedBox.Style'> .avatar { position: absolute; width: 26px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java index d06b59e..a8b6065 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -238,11 +238,11 @@ changeStatus = info.status(); info.revisions().copyKeysIntoChildren("name"); if (edit != null) { - edit.set_name(edit.commit().commit()); - info.set_edit(edit); + edit.setName(edit.commit().commit()); + info.setEdit(edit); info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit)); } - int currentPatchSet = info.revision(info.current_revision())._number(); + int currentPatchSet = info.revision(info.currentRevision())._number(); JsArray<RevisionInfo> list = info.revisions().values(); RevisionInfo.sortRevisionInfoByNumber(list); diffTable.set(prefs, list, diff, edit != null, currentPatchSet, @@ -570,8 +570,8 @@ diffTable.addStyleName(DiffTable.style.showLineNumbers()); } - cmA = newCM(diff.meta_a(), diff.text_a(), diffTable.cmA); - cmB = newCM(diff.meta_b(), diff.text_b(), diffTable.cmB); + cmA = newCM(diff.metaA(), diff.textA(), diffTable.cmA); + cmB = newCM(diff.metaB(), diff.textB(), diffTable.cmB); cmA.extras().side(DisplaySide.A); cmB.extras().side(DisplaySide.B); @@ -606,7 +606,7 @@ chunkManager.getLineMapper()); prefsAction = new PreferencesAction(this, prefs); - header.init(prefsAction, getLinks(), diff.side_by_side_web_links()); + header.init(prefsAction, getLinks(), diff.sideBySideWebLinks()); scrollSynchronizer.setAutoHideDiffTableHeader(prefs.autoHideDiffTableHeader()); if (prefs.syntaxHighlighting() && fileSize.compareTo(FileSize.SMALL) > 0) { @@ -654,7 +654,7 @@ } DiffInfo.IntraLineStatus getIntraLineStatus() { - return diff.intraline_status(); + return diff.intralineStatus(); } boolean canEnableRenderEntireFile(DiffPreferences prefs) { @@ -663,7 +663,7 @@ } String getContentType() { - return getContentType(diff.meta_b()); + return getContentType(diff.metaB()); } void setThemeStyles(boolean d) { @@ -716,8 +716,8 @@ @Override public void onSuccess(Void result) { if (prefs.syntaxHighlighting()) { - cmA.setOption("mode", getContentType(diff.meta_a())); - cmB.setOption("mode", getContentType(diff.meta_b())); + cmA.setOption("mode", getContentType(diff.metaA())); + cmB.setOption("mode", getContentType(diff.metaB())); } } @@ -923,7 +923,7 @@ int offset = 6; // Adjust for merge commits, which have two parent lines - if (diff.text_b().startsWith("Merge")) { + if (diff.textB().startsWith("Merge")) { offset += 1; } @@ -983,8 +983,8 @@ private String getContentType(DiffInfo.FileMeta meta) { if (prefs.syntaxHighlighting() && meta != null - && meta.content_type() != null) { - ModeInfo m = ModeInfo.findMode(meta.content_type(), path); + && meta.contentType() != null) { + ModeInfo m = ModeInfo.findMode(meta.contentType(), path); return m != null ? m.mime() : null; } return null; @@ -992,8 +992,8 @@ private void injectMode(DiffInfo diffInfo, AsyncCallback<Void> cb) { new ModeInjector() - .add(getContentType(diffInfo.meta_a())) - .add(getContentType(diffInfo.meta_b())) + .add(getContentType(diffInfo.metaA())) + .add(getContentType(diffInfo.metaB())) .inject(cb); } @@ -1043,8 +1043,8 @@ @Override public void onSuccess(DiffInfo info) { new ModeInjector() - .add(getContentType(info.meta_a())) - .add(getContentType(info.meta_b())) + .add(getContentType(info.metaA())) + .add(getContentType(info.metaB())) .inject(CallbackGroup.<Void> emptyCallback()); } @@ -1085,8 +1085,8 @@ } private static FileSize bucketFileSize(DiffInfo diff) { - FileMeta a = diff.meta_a(); - FileMeta b = diff.meta_b(); + FileMeta a = diff.metaA(); + FileMeta b = diff.metaB(); FileSize[] sizes = FileSize.values(); for (int i = sizes.length - 1; 0 <= i; i--) { FileSize s = sizes[i];
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml index da5b351..a4c2eb9 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml
@@ -17,7 +17,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:d='urn:import:com.google.gerrit.client.diff'> - <ui:style> + <ui:style gss='false'> .sbs { margin-left: -5px; margin-right: -5px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java index 4c12cc0..258eec6 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2013 The Android Open Source Project +// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml index 0ff23a7..bf3c425 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
@@ -16,7 +16,7 @@ --> <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style type='com.google.gerrit.client.diff.SkipBar.SkipBarStyle'> + <ui:style gss='false' type='com.google.gerrit.client.diff.SkipBar.SkipBarStyle'> .skipBar { background-color: #def; height: 1.3em;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java index 4972f5d..a344e6b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
@@ -45,7 +45,8 @@ JsArray<Region> regions = diff.content(); List<SkippedLine> skips = new ArrayList<>(); - int lineA = 0, lineB = 0; + int lineA = 0; + int lineB = 0; for (int i = 0; i < regions.length(); i++) { Region current = regions.get(i); if (current.ab() != null || current.common() || current.skip() > 0) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java index cea9106..d4237f9 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
@@ -15,8 +15,6 @@ package com.google.gerrit.client.download; import com.google.gerrit.client.Gerrit; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; import com.google.gerrit.reviewdb.client.Project; import com.google.gwt.aria.client.Roles; import com.google.gwt.event.dom.client.ClickEvent; @@ -25,89 +23,15 @@ import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Widget; import com.google.gwtexpui.clippy.client.CopyableLabel; -import com.google.gwtjsonrpc.common.AsyncCallback; -import com.google.gwtjsonrpc.common.VoidResult; public abstract class DownloadCommandLink extends Anchor implements ClickHandler { public static class CopyableCommandLinkFactory { protected CopyableLabel copyLabel = null; protected Widget widget; - public class CheckoutCommandLink extends DownloadCommandLink { - public CheckoutCommandLink () { - super(DownloadCommand.CHECKOUT, "checkout"); - } - - @Override - protected void setCurrentUrl(DownloadUrlLink link) { - widget.setVisible(true); - copyLabel.setText("git fetch " + link.getUrlData() - + " && git checkout FETCH_HEAD"); - } - } - - public class PullCommandLink extends DownloadCommandLink { - public PullCommandLink() { - super(DownloadCommand.PULL, "pull"); - } - - @Override - protected void setCurrentUrl(DownloadUrlLink link) { - widget.setVisible(true); - copyLabel.setText("git pull " + link.getUrlData()); - } - } - - public class CherryPickCommandLink extends DownloadCommandLink { - public CherryPickCommandLink() { - super(DownloadCommand.CHERRY_PICK, "cherry-pick"); - } - - @Override - protected void setCurrentUrl(DownloadUrlLink link) { - widget.setVisible(true); - copyLabel.setText("git fetch " + link.getUrlData() - + " && git cherry-pick FETCH_HEAD"); - } - } - - public class FormatPatchCommandLink extends DownloadCommandLink { - public FormatPatchCommandLink() { - super(DownloadCommand.FORMAT_PATCH, "patch"); - } - - @Override - protected void setCurrentUrl(DownloadUrlLink link) { - widget.setVisible(true); - copyLabel.setText("git fetch " + link.getUrlData() - + " && git format-patch -1 --stdout FETCH_HEAD"); - } - } - - public class RepoCommandLink extends DownloadCommandLink { - String projectName; - String ref; - public RepoCommandLink(String project, String ref) { - super(DownloadCommand.REPO_DOWNLOAD, "repo download"); - this.projectName = project; - this.ref = ref; - } - - @Override - protected void setCurrentUrl(DownloadUrlLink link) { - widget.setVisible(false); - final StringBuilder r = new StringBuilder(); - r.append("repo download "); - r.append(projectName); - r.append(" "); - r.append(ref); - copyLabel.setText(r.toString()); - } - } - public class CloneCommandLink extends DownloadCommandLink { public CloneCommandLink() { - super(DownloadCommand.CHECKOUT, "clone"); + super("clone"); } @Override @@ -121,7 +45,7 @@ private final Project.NameKey project; public CloneWithCommitMsgHookCommandLink(Project.NameKey project) { - super(DownloadCommand.CHECKOUT, "clone with commit-msg hook"); + super("clone with commit-msg hook"); this.project = project; } @@ -175,12 +99,8 @@ } } - final DownloadCommand cmdType; - - public DownloadCommandLink(DownloadCommand cmdType, - String text) { + public DownloadCommandLink(String text) { super(text); - this.cmdType = cmdType; setStyleName(Gerrit.RESOURCES.css().downloadLink()); Roles.getTabRole().set(getElement()); addClickHandler(this); @@ -192,28 +112,6 @@ event.stopPropagation(); select(); - - if (Gerrit.isSignedIn()) { - // If the user is signed-in, remember this choice for future panels. - // - AccountGeneralPreferences pref = - Gerrit.getUserAccount().getGeneralPreferences(); - pref.setDownloadCommand(cmdType); - com.google.gerrit.client.account.Util.ACCOUNT_SVC.changePreferences(pref, - new AsyncCallback<VoidResult>() { - @Override - public void onFailure(Throwable caught) { - } - - @Override - public void onSuccess(VoidResult result) { - } - }); - } - } - - public DownloadCommand getCmdType() { - return cmdType; } void select() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java index d17d6c2..5b7d015 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
@@ -15,8 +15,6 @@ package com.google.gerrit.client.download; import com.google.gerrit.client.Gerrit; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; import com.google.gwt.aria.client.Roles; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; @@ -34,7 +32,7 @@ return getWidgetCount() == 0; } - public void select(AccountGeneralPreferences.DownloadCommand cmdType) { + public void select() { DownloadCommandLink first = null; for (Widget w : this) { @@ -43,10 +41,6 @@ if (first == null) { first = d; } - if (d.cmdType == cmdType) { - d.select(); - return; - } } } @@ -70,9 +64,6 @@ private void update() { if (currentCommand != null && currentUrl != null) { currentCommand.setCurrentUrl(currentUrl); - } else if (currentCommand != null && - currentCommand.getCmdType().equals(DownloadCommand.REPO_DOWNLOAD)) { - currentCommand.setCurrentUrl(null); } } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java index 350dbed..19c65a9 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
@@ -16,32 +16,24 @@ import com.google.gerrit.client.Gerrit; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; -import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.InlineLabel; import com.google.gwtexpui.clippy.client.CopyableLabel; -import java.util.Set; - public abstract class DownloadPanel extends FlowPanel { protected String projectName; - protected Set<DownloadScheme> allowedSchemes = - Gerrit.getConfig().getDownloadSchemes(); - protected Set<DownloadCommand> allowedCommands = - Gerrit.getConfig().getDownloadCommands(); protected DownloadCommandLink.CopyableCommandLinkFactory cmdLinkfactory; protected DownloadCommandPanel commands = new DownloadCommandPanel(); protected DownloadUrlPanel urls = new DownloadUrlPanel(commands); protected CopyableLabel copyLabel = new CopyableLabel(""); - public DownloadPanel(String project, String ref, boolean allowAnonymous) { + public DownloadPanel(String project, boolean allowAnonymous) { this.projectName = project; copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadLinkCopyLabel()); - urls.add(DownloadUrlLink.createDownloadUrlLinks(project, ref, allowAnonymous)); + urls.add(DownloadUrlLink.createDownloadUrlLinks(project, allowAnonymous)); cmdLinkfactory = new DownloadCommandLink.CopyableCommandLinkFactory( copyLabel, urls); @@ -58,7 +50,7 @@ pref = new AccountGeneralPreferences(); pref.resetToDefaults(); } - commands.select(pref.getDownloadCommand()); + commands.select(); urls.select(pref.getDownloadUrl()); FlowPanel p = new FlowPanel();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java index ce5c060..275b918 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
@@ -17,7 +17,6 @@ import com.google.gerrit.client.Gerrit; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; -import com.google.gerrit.reviewdb.client.AuthType; import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; @@ -33,28 +32,9 @@ import java.util.Set; public class DownloadUrlLink extends Anchor implements ClickHandler { - public static class DownloadRefUrlLink extends DownloadUrlLink { - protected String projectName; - protected String ref; - - protected DownloadRefUrlLink(DownloadScheme urlType, - String text, String project, String ref) { - super(urlType, text); - this.projectName = project; - this.ref = ref; - } - - protected void appendRef(StringBuilder r) { - if (ref != null) { - r.append(" "); - r.append(ref); - } - } - } - - public static class AnonGitLink extends DownloadRefUrlLink { - public AnonGitLink(String project, String ref) { - super(DownloadScheme.ANON_GIT, Util.M.anonymousDownload("Git"), project, ref); + public static class AnonGitLink extends DownloadUrlLink { + public AnonGitLink(String project) { + super(DownloadScheme.ANON_GIT, Util.M.anonymousDownload("Git"), project); } @Override @@ -62,14 +42,13 @@ StringBuilder r = new StringBuilder(); r.append(Gerrit.getConfig().getGitDaemonUrl()); r.append(projectName); - appendRef(r); return r.toString(); } } - public static class AnonHttpLink extends DownloadRefUrlLink { - public AnonHttpLink(String project, String ref) { - super(DownloadScheme.ANON_HTTP, Util.M.anonymousDownload("HTTP"), project, ref); + public static class AnonHttpLink extends DownloadUrlLink { + public AnonHttpLink(String project) { + super(DownloadScheme.ANON_HTTP, Util.M.anonymousDownload("HTTP"), project); } @Override @@ -81,14 +60,13 @@ r.append(hostPageUrl); } r.append(projectName); - appendRef(r); return r.toString(); } } - public static class SshLink extends DownloadRefUrlLink { - public SshLink(String project, String ref) { - super(DownloadScheme.SSH, "SSH", project, ref); + public static class SshLink extends DownloadUrlLink { + public SshLink(String project) { + super(DownloadScheme.SSH, "SSH", project); } @Override @@ -107,16 +85,15 @@ r.append(sshAddr); r.append("/"); r.append(projectName); - appendRef(r); return r.toString(); } } - public static class HttpLink extends DownloadRefUrlLink { + public static class HttpLink extends DownloadUrlLink { protected boolean anonymous; - public HttpLink(String project, String ref, boolean anonymous) { - super(DownloadScheme.HTTP, "HTTP", project, ref); + public HttpLink(String project, boolean anonymous) { + super(DownloadScheme.HTTP, "HTTP", project); this.anonymous = anonymous; } @@ -145,46 +122,41 @@ r.append(base.substring(s)); } r.append(projectName); - appendRef(r); return r.toString(); } } public static boolean siteReliesOnHttp() { return Gerrit.getConfig().getGitHttpUrl() != null - && Gerrit.getConfig().getAuthType() == AuthType.CUSTOM_EXTENSION + && Gerrit.info().auth().isCustomExtension() && !Gerrit.getConfig().siteHasUsernames(); } public static List<DownloadUrlLink> createDownloadUrlLinks(String project, - String ref, boolean allowAnonymous) { + boolean allowAnonymous) { List<DownloadUrlLink> urls = new ArrayList<>(); - Set<DownloadScheme> allowedSchemes = Gerrit.getConfig().getDownloadSchemes(); + Set<String> allowedSchemes = Gerrit.info().download().schemes(); if (allowAnonymous && Gerrit.getConfig().getGitDaemonUrl() != null - && (allowedSchemes.contains(DownloadScheme.ANON_GIT) || - allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) { - urls.add(new DownloadUrlLink.AnonGitLink(project, ref)); + && allowedSchemes.contains("git")) { + urls.add(new DownloadUrlLink.AnonGitLink(project)); } if (allowAnonymous - && (allowedSchemes.contains(DownloadScheme.ANON_HTTP) || - allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) { - urls.add(new DownloadUrlLink.AnonHttpLink(project, ref)); + && allowedSchemes.contains("anonymous http")) { + urls.add(new DownloadUrlLink.AnonHttpLink(project)); } if (Gerrit.getConfig().getSshdAddress() != null && hasUserName() - && (allowedSchemes.contains(DownloadScheme.SSH) || - allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) { - urls.add(new DownloadUrlLink.SshLink(project, ref)); + && allowedSchemes.contains("ssh")) { + urls.add(new DownloadUrlLink.SshLink(project)); } if ((hasUserName() || siteReliesOnHttp()) - && (allowedSchemes.contains(DownloadScheme.HTTP) - || allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) { - urls.add(new DownloadUrlLink.HttpLink(project, ref, allowAnonymous)); + && allowedSchemes.contains("http")) { + urls.add(new DownloadUrlLink.HttpLink(project, allowAnonymous)); } return urls; } @@ -196,21 +168,11 @@ } protected DownloadScheme urlType; + protected String projectName; protected String urlData; protected String hostPageUrl = GWT.getHostPageBaseURL(); - public DownloadUrlLink(DownloadScheme urlType, String text, String urlData) { - this(text); - this.urlType = urlType; - this.urlData = urlData; - } - - public DownloadUrlLink(DownloadScheme urlType, String text) { - this(text); - this.urlType = urlType; - } - - public DownloadUrlLink(String text) { + public DownloadUrlLink(DownloadScheme urlType, String text, String project) { super(text); setStyleName(Gerrit.RESOURCES.css().downloadLink()); Roles.getTabRole().set(getElement()); @@ -219,6 +181,8 @@ if (!hostPageUrl.endsWith("/")) { hostPageUrl += "/"; } + this.urlType = urlType; + this.projectName = project; } public String getUrlData() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java index c833c5d..dda5fc2 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
@@ -19,7 +19,7 @@ import com.google.gwt.core.client.JsArray; public class EditFileInfo extends JavaScriptObject { - public final native JsArray<DiffWebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; protected EditFileInfo() { }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java index dd36657..e463607 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -185,7 +185,7 @@ .get(group1.add(new AsyncCallback<DiffInfo>() { @Override public void onSuccess(DiffInfo diffInfo) { - diffLinks = diffInfo.web_links(); + diffLinks = diffInfo.webLinks(); } @Override @@ -377,7 +377,7 @@ renderLinksToDiff(); if (editInfo != null) { - renderLinks(Natives.asList(editInfo.web_links())); + renderLinks(Natives.asList(editInfo.webLinks())); } else if (diffLinks != null) { renderLinks(Natives.asList(diffLinks)); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml index 93c3bb9..a82dc49 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
@@ -16,7 +16,7 @@ --> <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <ui:style> + <ui:style gss='false'> @external .CodeMirror, .CodeMirror-cursor; .header {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css index 8d961ca..0ee9fba 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -48,6 +48,10 @@ font-family: norm-font; } +button { + padding: 1px 6px; +} + .gerritBody { font-size: small; padding-left: 5px; @@ -297,13 +301,22 @@ white-space: nowrap; display: inline; } -.searchPanel .gwt-TextBox { +.searchPanel .searchTextBox { font-size: 9pt; + margin: 5.286px 3px 0 0; } -.searchPanel .gwt-Button { - font-size: 9pt; - margin-left: 2px; - padding: 3px 6px; +.searchPanel .searchButton { + text-align: center; + font-size: 8pt; + font-weight: bold; + cursor: pointer; + border: 2px solid; + color: #FFF; + border-color: rgba(0, 0, 0, 0.15); + height: 14px; + background-color: #53A93F; + border-radius: 2px; + box-sizing: content-box; } .suggestBoxPopup { z-index: 200; @@ -345,7 +358,7 @@ opacity: 0.80; } } -@if user.agent ie6 ie8 { +@if user.agent ie8 { /* IE just doesn't do opacity the way we want, make our dialog * stand out in a way that it can't be missed against the page */
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java index 0b178f2..e2b1112 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
@@ -125,7 +125,7 @@ } else { MemberInput input = MemberInput.create(); for (String member : members) { - input.add_member(member); + input.addMember(member); } members(group).post(input, cb); } @@ -139,7 +139,7 @@ } else { MemberInput in = MemberInput.create(); for (Integer id : ids) { - in.add_member(id.toString()); + in.addMember(id.toString()); } group(group).view("members.delete").post(in, cb); } @@ -172,7 +172,7 @@ } else { IncludedGroupInput input = IncludedGroupInput.create(); for (String includedGroup : includedGroups) { - input.add_group(includedGroup); + input.addGroup(includedGroup); } groups(group).post(input, cb); } @@ -187,7 +187,7 @@ } else { IncludedGroupInput in = IncludedGroupInput.create(); for (AccountGroup.UUID g : ids) { - in.add_group(g.get()); + in.addGroup(g.get()); } group(group).view("groups.delete").post(in, cb); } @@ -235,7 +235,7 @@ private static class MemberInput extends JavaScriptObject { final native void init() /*-{ this.members = []; }-*/; - final native void add_member(String n) /*-{ this.members.push(n); }-*/; + final native void addMember(String n) /*-{ this.members.push(n); }-*/; static MemberInput create() { MemberInput m = (MemberInput) createObject(); @@ -249,7 +249,7 @@ private static class IncludedGroupInput extends JavaScriptObject { final native void init() /*-{ this.groups = []; }-*/; - final native void add_group(String n) /*-{ this.groups.push(n); }-*/; + final native void addGroup(String n) /*-{ this.groups.push(n); }-*/; static IncludedGroupInput create() { IncludedGroupInput g = (IncludedGroupInput) createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java index efeb2ec..63823ea 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2008 The Android Open Source Project +// Copyright (C) 2008 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -546,7 +546,7 @@ private CommentEditorPanel findOrCreateCommentEditor(final int suggestRow, final int column, final PatchLineComment newComment, final boolean create) { int row = suggestRow; - int spans[] = new int[column + 1]; + int[] spans = new int[column + 1]; FIND_ROW: while (row < table.getRowCount()) { int col = 0; for (int cell = 0; row < table.getRowCount()
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java index 2538102..8642556 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
@@ -345,7 +345,7 @@ if (c.getLine() > 0) { i.line(c.getLine()); } - i.in_reply_to(c.getParentUuid()); + i.inReplyTo(c.getParentUuid()); i.message(c.getMessage()); return i; } @@ -359,7 +359,7 @@ i.id()), i.line(), Gerrit.getUserAccount().getId(), - i.in_reply_to(), + i.inReplyTo(), i.updated()); p.setMessage(i.message()); p.setSide((short) (i.side() == Side.PARENT ? 0 : 1));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommitMessageBlock.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommitMessageBlock.ui.xml index ca81537..f1bf3de 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommitMessageBlock.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommitMessageBlock.ui.xml
@@ -20,7 +20,7 @@ <ui:with field='res' type='com.google.gerrit.client.GerritResources'/> - <ui:style> + <ui:style gss='false'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor; @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/NavLinks.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/NavLinks.java index 2a04c38..352ade7 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/NavLinks.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/NavLinks.java
@@ -54,7 +54,7 @@ private final KeyCommandSet keys; private final Grid table; - private KeyCommand cmds[] = new KeyCommand[2]; + private KeyCommand[] cmds = new KeyCommand[2]; NavLinks(KeyCommandSet kcs, PatchSet.Id forPatch) { patchSetId = forPatch;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml index 586b767..5164302 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.ui.xml
@@ -22,7 +22,7 @@ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' ui:generateLocales='default,en' > -<ui:style> +<ui:style gss='false'> @external .gwt-TextBox; @external .gwt-ListBox;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml index 338e950..8977876 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml
@@ -18,7 +18,7 @@ <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> <ui:with field='res' type='com.google.gerrit.client.GerritResources'/> - <ui:style type='com.google.gerrit.client.patches.PatchSetSelectBox.BoxStyle'> + <ui:style gss='false' type='com.google.gerrit.client.patches.PatchSetSelectBox.BoxStyle'> @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java index a5c1484..2479322 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
@@ -251,7 +251,7 @@ } private List<WebLinkInfo> getWebLinks(DiffInfo diffInfo) { - return diffInfo.unified_web_links(); + return diffInfo.unifiedWebLinks(); } private String getSideBySideDiffUrl() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java index 6c1a841..0284aa3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
@@ -17,22 +17,20 @@ import com.google.gerrit.client.WebLinkInfo; import com.google.gerrit.client.actions.ActionInfo; import com.google.gerrit.client.rpc.NativeMap; -import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; public class BranchInfo extends JavaScriptObject { public final String getShortName() { - return ref().startsWith(Branch.R_HEADS) - ? ref().substring(Branch.R_HEADS.length()) - : ref(); + return RefNames.shortName(ref()); } public final native String ref() /*-{ return this.ref; }-*/; public final native String revision() /*-{ return this.revision; }-*/; public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/; public final native NativeMap<ActionInfo> actions() /*-{ return this.actions }-*/; - public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; protected BranchInfo() { }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java index 7aa5be5..b91c5de 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -35,23 +35,23 @@ public final native String description() /*-{ return this.description }-*/; - public final native InheritedBooleanInfo require_change_id() + public final native InheritedBooleanInfo requireChangeId() /*-{ return this.require_change_id; }-*/; - public final native InheritedBooleanInfo use_content_merge() + public final native InheritedBooleanInfo useContentMerge() /*-{ return this.use_content_merge; }-*/; - public final native InheritedBooleanInfo use_contributor_agreements() + public final native InheritedBooleanInfo useContributorAgreements() /*-{ return this.use_contributor_agreements; }-*/; - public final native InheritedBooleanInfo create_new_change_for_all_not_in_target() + public final native InheritedBooleanInfo createNewChangeForAllNotInTarget() /*-{ return this.create_new_change_for_all_not_in_target; }-*/; - public final native InheritedBooleanInfo use_signed_off_by() + public final native InheritedBooleanInfo useSignedOffBy() /*-{ return this.use_signed_off_by; }-*/; - public final SubmitType submit_type() { - return SubmitType.valueOf(submit_typeRaw()); + public final SubmitType submitType() { + return SubmitType.valueOf(submitTypeRaw()); } public final native NativeMap<NativeMap<ConfigParameterInfo>> pluginConfig() @@ -63,7 +63,7 @@ public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/; - private final native String submit_typeRaw() + private final native String submitTypeRaw() /*-{ return this.submit_type }-*/; public final ProjectState state() { @@ -75,7 +75,7 @@ private final native String stateRaw() /*-{ return this.state }-*/; - public final native MaxObjectSizeLimitInfo max_object_size_limit() + public final native MaxObjectSizeLimitInfo maxObjectSizeLimit() /*-{ return this.max_object_size_limit; }-*/; private final native NativeMap<CommentLinkInfo> commentlinks0() @@ -131,13 +131,13 @@ public final native boolean value() /*-{ return this.value ? true : false; }-*/; - public final native boolean inherited_value() + public final native boolean inheritedValue() /*-{ return this.inherited_value ? true : false; }-*/; - public final InheritableBoolean configured_value() { - return InheritableBoolean.valueOf(configured_valueRaw()); + public final InheritableBoolean configuredValue() { + return InheritableBoolean.valueOf(configuredValueRaw()); } - private final native String configured_valueRaw() + private final native String configuredValueRaw() /*-{ return this.configured_value }-*/; public final void setConfiguredValue(InheritableBoolean v) { @@ -152,8 +152,8 @@ public static class MaxObjectSizeLimitInfo extends JavaScriptObject { public final native String value() /*-{ return this.value; }-*/; - public final native String inherited_value() /*-{ return this.inherited_value; }-*/; - public final native String configured_value() /*-{ return this.configured_value }-*/; + public final native String inheritedValue() /*-{ return this.inherited_value; }-*/; + public final native String configuredValue() /*-{ return this.configured_value }-*/; protected MaxObjectSizeLimitInfo() { } @@ -179,8 +179,8 @@ public static class ConfigParameterValue extends JavaScriptObject { final native void init() /*-{ this.values = []; }-*/; - final native void add_value(String v) /*-{ this.values.push(v); }-*/; - final native void set_value(String v) /*-{ if(v)this.value = v; }-*/; + final native void addValue(String v) /*-{ this.values.push(v); }-*/; + final native void setValue(String v) /*-{ if(v)this.value = v; }-*/; public static ConfigParameterValue create() { ConfigParameterValue v = createObject().cast(); return v; @@ -189,13 +189,13 @@ public final ConfigParameterValue values(String[] values) { init(); for (String v : values) { - add_value(v); + addValue(v); } return this; } public final ConfigParameterValue value(String v) { - set_value(v); + setValue(v); return this; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java index c22b007..e000a97 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
@@ -61,7 +61,7 @@ } public static void add(ChangeInfo info) { - instance.changeToProject.put(info.legacy_id().get(), info.project()); + instance.changeToProject.put(info.legacyId().get(), info.project()); } private final LinkedHashMap<String, Entry> cache;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java index b121d07..d81dfe5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -84,7 +84,7 @@ } else { DeleteBranchesInput d = DeleteBranchesInput.create(); for (String ref : refs) { - d.add_branch(ref); + d.addBranch(ref); } project(name).view("branches:delete").post(d, cb); } @@ -317,6 +317,6 @@ } final native void init() /*-{ this.branches = []; }-*/; - final native void add_branch(String b) /*-{ this.branches.push(b); }-*/; + final native void addBranch(String b) /*-{ this.branches.push(b); }-*/; } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java index fe9872c..029e59b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
@@ -30,7 +30,7 @@ public final native String name() /*-{ return this.name; }-*/; public final native String description() /*-{ return this.description; }-*/; - public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/; + public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/; public final ProjectState state() { return ProjectState.valueOf(getStringState());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java index cdad972..2633e3b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
@@ -71,8 +71,8 @@ return ai.email(); } else if (ai.name() != null) { return ai.name(); - } else if (ai._account_id() != 0) { - return "" + ai._account_id(); + } else if (ai._accountId() != 0) { + return "" + ai._accountId(); } else { return ""; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java index c9a0590..6af4b78 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
@@ -17,9 +17,9 @@ import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.changes.QueryScreen; import com.google.gerrit.common.PageLinks; -import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; /** Link to the open changes of a project. */ public class BranchLink extends InlineHyperlink { @@ -61,10 +61,10 @@ String branch, String topic) { String query = PageLinks.projectQuery(project, status); - if (branch.startsWith(Branch.R_REFS)) { - if (branch.startsWith(Branch.R_HEADS)) { + if (branch.startsWith(RefNames.REFS)) { + if (branch.startsWith(RefNames.REFS_HEADS)) { query += " " + PageLinks.op("branch", // - branch.substring(Branch.R_HEADS.length())); + branch.substring(RefNames.REFS_HEADS.length())); } else { query += " " + PageLinks.op("ref", branch); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java index 00269f9..f69e042 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java
@@ -82,7 +82,7 @@ return newBranch.getText(); } - class BranchSuggestion implements Suggestion { + static class BranchSuggestion implements Suggestion { private BranchInfo branch; public BranchSuggestion(BranchInfo branch) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java index 021f39c..a2b4aa8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java
@@ -19,7 +19,6 @@ import com.google.gerrit.client.projects.ProjectApi; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.Natives; -import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Project; import com.google.gwt.core.client.JsArray; import com.google.gwt.user.client.ui.FlowPanel; @@ -82,7 +81,7 @@ return newChange.getText(); } - class BranchSuggestion implements Suggestion { + static class BranchSuggestion implements Suggestion { private BranchInfo branch; public BranchSuggestion(BranchInfo branch) { @@ -91,10 +90,7 @@ @Override public String getDisplayString() { - if (branch.ref().startsWith(Branch.R_HEADS)) { - return branch.ref().substring(Branch.R_HEADS.length()); - } - return branch.ref(); + return branch.getShortName(); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java similarity index 96% rename from gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java index 59feba8..76ad0e7 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java
@@ -21,7 +21,7 @@ import com.google.gwtexpui.safehtml.client.SafeHtml; import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; -public class FancyFlexTableImplIE6 extends FancyFlexTableImpl { +public class FancyFlexTableImplIE8 extends FancyFlexTableImpl { @Override public void resetHtml(final FlexTable myTable, final SafeHtml bodyHtml) { final Element oldBody = getBodyElement(myTable);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java index 6637957..bb55b69 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
@@ -65,8 +65,9 @@ } public void displaySubset(ProjectMap projects, int fromIndex, int toIndex) { - while (1 < table.getRowCount()) + while (1 < table.getRowCount()) { table.removeRow(table.getRowCount() - 1); + } List<ProjectInfo> list = Natives.asList(projects.values()); Collections.sort(list, new Comparator<ProjectInfo>() { @@ -75,8 +76,9 @@ return a.name().compareTo(b.name()); } }); - for(ProjectInfo p : list.subList(fromIndex, toIndex)) + for (ProjectInfo p : list.subList(fromIndex, toIndex)) { insert(table.getRowCount(), p); + } finishDisplay(); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java index 5f47d98..2744877 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
@@ -20,6 +20,7 @@ import com.google.gerrit.client.changes.Util; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.Natives; +import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.reviewdb.client.Change; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -29,6 +30,7 @@ import com.google.gwtexpui.globalkey.client.GlobalKey; import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -51,10 +53,10 @@ String query = request.getQuery().toLowerCase(); LinkedList<ChangeSuggestion> suggestions = new LinkedList<>(); for (final ChangeInfo ci : changes) { - if (changeId.equals(ci.legacy_id())) { + if (changeId.equals(ci.legacyId())) { continue; // do not suggest current change } - String id = String.valueOf(ci.legacy_id().get()); + String id = String.valueOf(ci.legacyId().get()); if (id.contains(query) || ci.subject().toLowerCase().contains(query)) { suggestions.add(new ChangeSuggestion(ci)); if (suggestions.size() >= 50) { // limit to 50 suggestions @@ -76,8 +78,10 @@ public void onClick(ClickEvent event) { boolean checked = ((CheckBox) event.getSource()).getValue(); if (checked) { - ChangeList.next("project:" + project + " AND branch:" + branch - + " AND is:open NOT age:90d", 0, 1000, + ChangeList.query( + "project:" + project + " AND branch:" + branch + + " AND is:open NOT age:90d", + Collections.<ListChangesOption> emptySet(), new GerritCallback<ChangeList>() { @Override public void onSuccess(ChangeList result) { @@ -136,12 +140,12 @@ @Override public String getDisplayString() { - return String.valueOf(change.legacy_id().get()) + ": " + change.subject(); + return String.valueOf(change.legacyId().get()) + ": " + change.subject(); } @Override public String getReplacementString() { - return String.valueOf(change.legacy_id().get()); + return String.valueOf(change.legacyId().get()); } } }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java index 1c9ad3c..639e5e7 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -112,9 +112,24 @@ }-*/; public enum LineClassWhere { - TEXT { @Override String value() { return "text"; } }, - BACKGROUND { @Override String value() { return "background"; } }, - WRAP { @Override String value() { return "wrap"; } }; + TEXT { + @Override + String value() { + return "text"; + } + }, + BACKGROUND { + @Override + String value() { + return "background"; + } + }, + WRAP { + @Override + String value() { + return "wrap"; + } + }; abstract String value(); }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java index 2d69015..eac8510 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
@@ -35,8 +35,8 @@ public static FromTo create(CommentRange range) { return create( - Pos.create(range.start_line() - 1, range.start_character()), - Pos.create(range.end_line() - 1, range.end_character())); + Pos.create(range.startLine() - 1, range.startCharacter()), + Pos.create(range.endLine() - 1, range.endCharacter())); } public final native Pos from() /*-{ return this.from }-*/;
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java index d6b194b..782500d 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
@@ -62,6 +62,7 @@ Modes.I.php(), Modes.I.pig(), Modes.I.properties(), + Modes.I.puppet(), Modes.I.python(), Modes.I.r(), Modes.I.rst(),
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java index e511be5..8170c2b 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
@@ -47,6 +47,7 @@ @Source("php.js") @DoNotEmbed DataResource php(); @Source("pig.js") @DoNotEmbed DataResource pig(); @Source("properties.js") @DoNotEmbed DataResource properties(); + @Source("puppet.js") @DoNotEmbed DataResource puppet(); @Source("python.js") @DoNotEmbed DataResource python(); @Source("r.js") @DoNotEmbed DataResource r(); @Source("rst.js") @DoNotEmbed DataResource rst();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java index 91fb6af..378c4d5 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -52,8 +52,6 @@ */ @Singleton class ContainerAuthFilter implements Filter { - public static final String REALM_NAME = "Gerrit Code Review"; - private final DynamicItem<WebSession> session; private final AccountCache accountCache; private final Config config;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java index 4b0e46c..eadc536 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -16,22 +16,18 @@ import com.google.common.base.Function; import com.google.common.base.Optional; +import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.gerrit.common.data.GerritConfig; import com.google.gerrit.common.data.GitwebConfig; -import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.server.account.Realm; import com.google.gerrit.server.change.ArchiveFormat; import com.google.gerrit.server.change.GetArchive; -import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.ConfigUtil; -import com.google.gerrit.server.config.DownloadConfig; import com.google.gerrit.server.config.GerritServerConfig; -import com.google.gerrit.server.contact.ContactStore; -import com.google.gerrit.server.mail.EmailSender; import com.google.gerrit.server.ssh.SshInfo; import com.google.inject.Inject; import com.google.inject.Provider; @@ -40,8 +36,6 @@ import org.eclipse.jgit.lib.Config; import java.net.MalformedURLException; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.TimeUnit; import javax.servlet.ServletContext; @@ -50,42 +44,32 @@ private final Realm realm; private final Config cfg; private final AuthConfig authConfig; - private final DownloadConfig downloadConfig; private final GetArchive.AllowedFormats archiveFormats; private final GitWebConfig gitWebConfig; - private final AllProjectsName wildProject; private final SshInfo sshInfo; - private EmailSender emailSender; - private final ContactStore contactStore; private final ServletContext servletContext; private final String anonymousCowardName; @Inject - GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc, - final AuthConfig ac, final GitWebConfig gwc, final AllProjectsName wp, - final SshInfo si, final ContactStore cs, - final ServletContext sc, final DownloadConfig dc, - final GetArchive.AllowedFormats af, - final @AnonymousCowardName String acn) { + GerritConfigProvider(Realm r, + @GerritServerConfig Config gsc, + AuthConfig ac, + GitWebConfig gwc, + SshInfo si, + ServletContext sc, + GetArchive.AllowedFormats af, + @AnonymousCowardName String acn) { realm = r; cfg = gsc; authConfig = ac; - downloadConfig = dc; archiveFormats = af; gitWebConfig = gwc; sshInfo = si; - wildProject = wp; - contactStore = cs; servletContext = sc; anonymousCowardName = acn; } - @Inject(optional = true) - void setEmailSender(final EmailSender d) { - emailSender = d; - } - private GerritConfig create() throws MalformedURLException { final GerritConfig config = new GerritConfig(); switch (authConfig.getAuthType()) { @@ -118,15 +102,9 @@ break; } config.setSwitchAccountUrl(cfg.getString("auth", null, "switchAccountUrl")); - config.setUseContributorAgreements(cfg.getBoolean("auth", - "contributoragreements", false)); config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl")); config.setGitHttpUrl(cfg.getString("gerrit", null, "gitHttpUrl")); - config.setUseContactInfo(contactStore != null && contactStore.isEnabled()); - config.setDownloadSchemes(downloadConfig.getDownloadSchemes()); - config.setDownloadCommands(downloadConfig.getDownloadCommands()); config.setAuthType(authConfig.getAuthType()); - config.setWildProject(wildProject); config.setDocumentationAvailable(servletContext .getResource("/Documentation/index.html") != null); config.setAnonymousCowardName(anonymousCowardName); @@ -134,8 +112,18 @@ config.setChangeUpdateDelay((int) ConfigUtil.getTimeUnit( cfg, "change", null, "updateDelay", 30, TimeUnit.SECONDS)); config.setLargeChangeSize(cfg.getInt("change", "largeChange", 500)); + + // Zip is not supported because it may be interpreted by a Java plugin as a + // valid JAR file, whose code would have access to cookies on the domain. config.setArchiveFormats(Lists.newArrayList(Iterables.transform( - archiveFormats.getAllowed(), + Iterables.filter( + archiveFormats.getAllowed(), + new Predicate<ArchiveFormat>() { + @Override + public boolean apply(ArchiveFormat format) { + return (format != ArchiveFormat.ZIP); + } + }), new Function<ArchiveFormat, String>() { @Override public String apply(ArchiveFormat in) { @@ -146,17 +134,7 @@ config.setReportBugUrl(cfg.getString("gerrit", null, "reportBugUrl")); config.setReportBugText(cfg.getString("gerrit", null, "reportBugText")); - final Set<Account.FieldName> fields = new HashSet<>(); - for (final Account.FieldName n : Account.FieldName.values()) { - if (realm.allowsEdit(n)) { - fields.add(n); - } - } - if (emailSender != null && emailSender.isEnabled() - && realm.allowsEdit(Account.FieldName.REGISTER_NEW_EMAIL)) { - fields.add(Account.FieldName.REGISTER_NEW_EMAIL); - } - config.setEditableAccountFields(fields); + config.setEditableAccountFields(realm.getEditableFields()); if (gitWebConfig.getUrl() != null) { config.setGitwebLink(new GitwebConfig(gitWebConfig.getUrl(), gitWebConfig
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java index 9d47977..7dc0c17 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
@@ -14,6 +14,9 @@ package com.google.gerrit.httpd; +import static java.nio.file.Files.isExecutable; +import static java.nio.file.Files.isRegularFile; + import com.google.gerrit.common.data.GitWebType; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; @@ -23,16 +26,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; public class GitWebConfig { private static final Logger log = LoggerFactory.getLogger(GitWebConfig.class); private final String url; - private final File gitweb_cgi; - private final File gitweb_css; - private final File gitweb_js; - private final File git_logo_png; + private final Path gitweb_cgi; + private final Path gitweb_css; + private final Path gitweb_js; + private final Path git_logo_png; private GitWebType type; @Inject @@ -117,20 +121,20 @@ return; } - final File pkgCgi = new File("/usr/lib/cgi-bin/gitweb.cgi"); + final Path pkgCgi = Paths.get("/usr/lib/cgi-bin/gitweb.cgi"); String[] resourcePaths = {"/usr/share/gitweb/static", "/usr/share/gitweb", "/var/www/static", "/var/www"}; - File cgi; + Path cgi; if (cfgCgi != null) { // Use the CGI script configured by the administrator, failing if it // cannot be used as specified. // cgi = sitePaths.resolve(cfgCgi); - if (!cgi.isFile()) { + if (!isRegularFile(cgi)) { throw new IllegalStateException("Cannot find gitweb.cgi: " + cgi); } - if (!cgi.canExecute()) { + if (!isExecutable(cgi)) { throw new IllegalStateException("Cannot execute gitweb.cgi: " + cgi); } @@ -138,11 +142,11 @@ // Assume the administrator pointed us to the distribution, // which also has the corresponding CSS and logo file. // - String absPath = cgi.getParentFile().getAbsolutePath(); + String absPath = cgi.getParent().toAbsolutePath().toString(); resourcePaths = new String[] {absPath + "/static", absPath}; } - } else if (pkgCgi.isFile() && pkgCgi.canExecute()) { + } else if (isRegularFile(pkgCgi) && isExecutable(pkgCgi)) { // Use the OS packaged CGI. // log.debug("Assuming gitweb at " + pkgCgi); @@ -154,13 +158,15 @@ resourcePaths = new String[] {}; } - File css = null, js = null, logo = null; + Path css = null; + Path js = null; + Path logo = null; for (String path : resourcePaths) { - File dir = new File(path); - css = new File(dir, "gitweb.css"); - js = new File(dir, "gitweb.js"); - logo = new File(dir, "git-logo.png"); - if (css.isFile() && logo.isFile()) { + Path dir = Paths.get(path); + css = dir.resolve("gitweb.css"); + js = dir.resolve("gitweb.js"); + logo = dir.resolve("git-logo.png"); + if (isRegularFile(css) && isRegularFile(logo)) { break; } } @@ -191,22 +197,22 @@ } /** @return local path to the CGI executable; null if we shouldn't execute. */ - public File getGitwebCGI() { + public Path getGitwebCGI() { return gitweb_cgi; } /** @return local path of the {@code gitweb.css} matching the CGI. */ - public File getGitwebCSS() { + public Path getGitwebCSS() { return gitweb_css; } /** @return local path of the {@code gitweb.js} for the CGI. */ - public File getGitwebJS() { + public Path getGitwebJS() { return gitweb_js; } /** @return local path of the {@code git-logo.png} for the CGI. */ - public File getGitLogoPNG() { + public Path getGitLogoPNG() { return git_logo_png; }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java index 1a2d3f6..1eb88b1 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd; +import com.google.common.io.ByteStreams; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -21,14 +23,14 @@ import org.xml.sax.SAXException; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.zip.GZIPOutputStream; import javax.xml.parsers.DocumentBuilder; @@ -36,7 +38,6 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; @@ -49,21 +50,21 @@ /** Utility functions to deal with HTML using W3C DOM operations. */ public class HtmlDomUtil { /** Standard character encoding we prefer (UTF-8). */ - public static final String ENC = "UTF-8"; + public static final Charset ENC = StandardCharsets.UTF_8; /** DOCTYPE for a standards mode HTML document. */ public static final String HTML_STRICT = "-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd"; /** Convert a document to a UTF-8 byte sequence. */ - public static byte[] toUTF8(final Document hostDoc) throws IOException { + public static byte[] toUTF8(Document hostDoc) throws IOException { return toString(hostDoc).getBytes(ENC); } /** Compress the document. */ - public static byte[] compress(final byte[] raw) throws IOException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final GZIPOutputStream gz = new GZIPOutputStream(out); + public static byte[] compress(byte[] raw) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gz = new GZIPOutputStream(out); gz.write(raw); gz.finish(); gz.flush(); @@ -71,43 +72,39 @@ } /** Convert a document to a String, assuming later encoding to UTF-8. */ - public static String toString(final Document hostDoc) throws IOException { + public static String toString(Document hostDoc) throws IOException { try { - final StringWriter out = new StringWriter(); - final DOMSource domSource = new DOMSource(hostDoc); - final StreamResult streamResult = new StreamResult(out); - final TransformerFactory tf = TransformerFactory.newInstance(); - final Transformer serializer = tf.newTransformer(); - serializer.setOutputProperty(OutputKeys.ENCODING, ENC); + StringWriter out = new StringWriter(); + DOMSource domSource = new DOMSource(hostDoc); + StreamResult streamResult = new StreamResult(out); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer serializer = tf.newTransformer(); + serializer.setOutputProperty(OutputKeys.ENCODING, ENC.name()); serializer.setOutputProperty(OutputKeys.METHOD, "html"); serializer.setOutputProperty(OutputKeys.INDENT, "no"); serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, HtmlDomUtil.HTML_STRICT); serializer.transform(domSource, streamResult); return out.toString(); - } catch (TransformerConfigurationException e) { - final IOException r = new IOException("Error transforming page"); - r.initCause(e); - throw r; } catch (TransformerException e) { - final IOException r = new IOException("Error transforming page"); + IOException r = new IOException("Error transforming page"); r.initCause(e); throw r; } } /** Find an element by its "id" attribute; null if no element is found. */ - public static Element find(final Node parent, final String name) { - final NodeList list = parent.getChildNodes(); + public static Element find(Node parent, String name) { + NodeList list = parent.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { - final Node n = list.item(i); + Node n = list.item(i); if (n instanceof Element) { - final Element e = (Element) n; + Element e = (Element) n; if (name.equals(e.getAttribute("id"))) { return e; } } - final Element r = find(n, name); + Element r = find(n, name); if (r != null) { return r; } @@ -116,9 +113,8 @@ } /** Append an HTML <input type="hidden"> to the form. */ - public static void addHidden(final Element form, final String name, - final String value) { - final Element in = form.getOwnerDocument().createElement("input"); + public static void addHidden(Element form, String name, String value) { + Element in = form.getOwnerDocument().createElement("input"); in.setAttribute("type", "hidden"); in.setAttribute("name", name); in.setAttribute("value", value); @@ -135,51 +131,38 @@ } /** Clone a document so it can be safely modified on a per-request basis. */ - public static Document clone(final Document doc) throws IOException { - final Document d; + public static Document clone(Document doc) throws IOException { + Document d; try { d = newBuilder().newDocument(); } catch (ParserConfigurationException e) { throw new IOException("Cannot clone document"); } - final Node n = d.importNode(doc.getDocumentElement(), true); + Node n = d.importNode(doc.getDocumentElement(), true); d.appendChild(n); return d; } /** Parse an XHTML file from our CLASSPATH and return the instance. */ - public static Document parseFile(final Class<?> context, final String name) + public static Document parseFile(Class<?> context, String name) throws IOException { - final InputStream in; - - in = context.getResourceAsStream(name); - if (in == null) { - return null; - } - try { - try { - try { - final Document doc = newBuilder().parse(in); - compact(doc); - return doc; - } catch (SAXException e) { - throw new IOException("Error reading " + name, e); - } catch (ParserConfigurationException e) { - throw new IOException("Error reading " + name, e); - } - } finally { - in.close(); + try (InputStream in = context.getResourceAsStream(name)) { + if (in == null) { + return null; } - } catch (IOException e) { + Document doc = newBuilder().parse(in); + compact(doc); + return doc; + } catch (SAXException | ParserConfigurationException | IOException e) { throw new IOException("Error reading " + name, e); } } - private static void compact(final Document doc) { + private static void compact(Document doc) { try { - final String expr = "//text()[normalize-space(.) = '']"; - final XPathFactory xp = XPathFactory.newInstance(); - final XPathExpression e = xp.newXPath().compile(expr); + String expr = "//text()[normalize-space(.) = '']"; + XPathFactory xp = XPathFactory.newInstance(); + XPathExpression e = xp.newXPath().compile(expr); NodeList empty = (NodeList) e.evaluate(doc, XPathConstants.NODESET); for (int i = 0; i < empty.getLength(); i++) { Node node = empty.item(i); @@ -191,78 +174,50 @@ } /** Read a Read a UTF-8 text file from our CLASSPATH and return it. */ - public static String readFile(final Class<?> context, final String name) + public static String readFile(Class<?> context, String name) throws IOException { - final InputStream in = context.getResourceAsStream(name); - if (in == null) { - return null; - } - try { - return asString(in); + try (InputStream in = context.getResourceAsStream(name)) { + if (in == null) { + return null; + } + return new String(ByteStreams.toByteArray(in), ENC); } catch (IOException e) { throw new IOException("Error reading " + name, e); } } /** Parse an XHTML file from the local drive and return the instance. */ - public static Document parseFile(final File path) throws IOException { - try { - final InputStream in = new FileInputStream(path); - try { - try { - final Document doc = newBuilder().parse(in); - compact(doc); - return doc; - } catch (SAXException e) { - throw new IOException("Error reading " + path, e); - } catch (ParserConfigurationException e) { - throw new IOException("Error reading " + path, e); - } - } finally { - in.close(); - } - } catch (FileNotFoundException e) { + public static Document parseFile(Path path) throws IOException { + try (InputStream in = Files.newInputStream(path)) { + Document doc = newBuilder().parse(in); + compact(doc); + return doc; + } catch (NoSuchFileException e) { return null; - } catch (IOException e) { + } catch (SAXException | ParserConfigurationException | IOException e) { throw new IOException("Error reading " + path, e); } } /** Read a UTF-8 text file from the local drive. */ - public static String readFile(final File parentDir, final String name) + public static String readFile(Path parentDir, String name) throws IOException { if (parentDir == null) { return null; } - final File path = new File(parentDir, name); - try { - return asString(new FileInputStream(path)); - } catch (FileNotFoundException e) { + Path path = parentDir.resolve(name); + try (InputStream in = Files.newInputStream(path)) { + return new String(ByteStreams.toByteArray(in), ENC); + } catch (NoSuchFileException e) { return null; } catch (IOException e) { throw new IOException("Error reading " + path, e); } } - private static String asString(final InputStream in) - throws UnsupportedEncodingException, IOException { - try { - final StringBuilder w = new StringBuilder(); - final InputStreamReader r = new InputStreamReader(in, ENC); - final char[] buf = new char[512]; - int n; - while ((n = r.read(buf)) > 0) { - w.append(buf, 0, n); - } - return w.toString(); - } finally { - in.close(); - } - } - private static DocumentBuilder newBuilder() throws ParserConfigurationException { - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setExpandEntityReferences(false); factory.setIgnoringComments(true);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java index 8c469a9..400b146 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -186,7 +186,7 @@ return MoreObjects.firstNonNull(req.getCharacterEncoding(), "UTF-8"); } - class Response extends HttpServletResponseWrapper { + static class Response extends HttpServletResponseWrapper { private static final String WWW_AUTHENTICATE = "WWW-Authenticate"; Response(HttpServletResponse rsp) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java index 12de344..33b9fed 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java
@@ -260,8 +260,12 @@ } else { int space = auth.indexOf(' ', eq + 1); int comma = auth.indexOf(',', eq + 1); - if (space < 0) space = auth.length(); - if (comma < 0) comma = auth.length(); + if (space < 0) { + space = auth.length(); + } + if (comma < 0) { + comma = auth.length(); + } final int e = Math.min(space, comma); value = auth.substring(eq + 1, e);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java index b8a8092..c1c3b2b 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -54,8 +54,6 @@ @SuppressWarnings("serial") @Singleton class BecomeAnyAccountLoginServlet extends HttpServlet { - private static final boolean IS_DEV = Boolean.getBoolean("Gerrit.GwtDevMode"); - private final SchemaFactory<ReviewDb> schema; private final DynamicItem<WebSession> webSession; private final AccountManager accountManager; @@ -104,7 +102,7 @@ throw new ServletException(e); } rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(raw.length); final OutputStream out = rsp.getOutputStream(); try { @@ -120,14 +118,6 @@ final StringBuilder rdr = new StringBuilder(); rdr.append(req.getContextPath()); rdr.append("/"); - if (IS_DEV && req.getParameter("gwt.codesvr") != null) { - if (rdr.indexOf("?") < 0) { - rdr.append("?"); - } else { - rdr.append("&"); - } - rdr.append("gwt.codesvr=").append(req.getParameter("gwt.codesvr")); - } if (res.isNew()) { rdr.append('#' + PageLinks.REGISTER); @@ -138,7 +128,7 @@ } else { rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); final Writer out = rsp.getWriter(); out.write("<html>"); out.write("<body>"); @@ -155,12 +145,6 @@ if (doc == null) { throw new FileNotFoundException("No " + pageName + " in webapp"); } - if (!IS_DEV) { - final Element devmode = HtmlDomUtil.find(doc, "gwtdevmode"); - if (devmode != null) { - devmode.getParentNode().removeChild(devmode); - } - } Element userlistElement = HtmlDomUtil.find(doc, "userlist"); ReviewDb db = schema.open();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java index 19c8342..b5400b2 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -110,7 +110,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertLoginServlet.java index 8652ef0..4c7ce7b 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertLoginServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpsClientSslCertLoginServlet.java
@@ -1,4 +1,4 @@ -//Copyright (C) 2011 The Android Open Source Project +// Copyright (C) 2011 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java index 41aa552..3396f2b 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java
@@ -14,16 +14,19 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.io.ByteStreams; import com.google.gerrit.httpd.GitWebConfig; import com.google.gwtexpui.server.CacheHeaders; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.jgit.util.IO; - -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -38,16 +41,16 @@ private final byte[] raw; @Inject - GitLogoServlet(final GitWebConfig gitWebConfig) throws IOException { + GitLogoServlet(GitWebConfig gitWebConfig) throws IOException { byte[] png; - final File src = gitWebConfig.getGitLogoPNG(); + Path src = gitWebConfig.getGitLogoPNG(); if (src != null) { - try { - png = IO.readFully(src); - } catch (FileNotFoundException e) { + try (InputStream in = Files.newInputStream(src)) { + png = ByteStreams.toByteArray(in); + } catch (NoSuchFileException e) { png = null; } - modified = src.lastModified(); + modified = lastModified(src); } else { modified = -1; png = null;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java index 4a39b97..5625334 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.gerrit.httpd.GitWebConfig; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.SitePaths; @@ -22,8 +24,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -55,14 +57,14 @@ private final byte[] raw_css; private final byte[] gz_css; - GitWebCssServlet(final File src) + GitWebCssServlet(final Path src) throws IOException { if (src != null) { - final File dir = src.getParentFile(); - final String name = src.getName(); + final Path dir = src.getParent(); + final String name = src.getFileName().toString(); final String raw = HtmlDomUtil.readFile(dir, name); if (raw != null) { - modified = src.lastModified(); + modified = lastModified(src); raw_css = raw.getBytes(ENC); gz_css = HtmlDomUtil.compress(raw_css); } else {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java index d71732a..6926afd 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java
@@ -14,16 +14,19 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.io.ByteStreams; import com.google.gerrit.httpd.GitWebConfig; import com.google.gwtexpui.server.CacheHeaders; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.jgit.util.IO; - -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -40,14 +43,14 @@ @Inject GitWebJavaScriptServlet(final GitWebConfig gitWebConfig) throws IOException { byte[] png; - final File src = gitWebConfig.getGitwebJS(); + Path src = gitWebConfig.getGitwebJS(); if (src != null) { - try { - png = IO.readFully(src); - } catch (FileNotFoundException e) { + try (InputStream in = Files.newInputStream(src)) { + png = ByteStreams.toByteArray(in); + } catch (NoSuchFileException e) { png = null; } - modified = src.lastModified(); + modified = lastModified(src); } else { modified = -1; png = null;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java index 573725c..cf43041 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java
@@ -29,6 +29,8 @@ package com.google.gerrit.httpd.gitweb; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.data.GerritConfig; import com.google.gerrit.extensions.restapi.Url; @@ -55,7 +57,6 @@ import java.io.BufferedReader; import java.io.EOFException; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -63,6 +64,8 @@ import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -84,7 +87,7 @@ private final Set<String> deniedActions; private final int bufferSize = 8192; - private final File gitwebCgi; + private final Path gitwebCgi; private final URI gitwebUrl; private final LocalDiskRepositoryManager repoManager; private final ProjectControl.Factory projectControl; @@ -145,28 +148,30 @@ private void makeSiteConfig(final SitePaths site, final GerritConfig gerritConfig) throws IOException { - if (!site.tmp_dir.exists()) { - site.tmp_dir.mkdirs(); + if (!Files.exists(site.tmp_dir)) { + Files.createDirectories(site.tmp_dir); } - File myconf = File.createTempFile("gitweb_config", ".perl", site.tmp_dir); + Path myconf = Files.createTempFile(site.tmp_dir, "gitweb_config", ".perl"); // To make our configuration file only readable or writable by us; // this reduces the chances of someone tampering with the file. // - myconf.setWritable(false, false /* all */); - myconf.setReadable(false, false /* all */); - myconf.setExecutable(false, false /* all */); + // TODO(dborowitz): Is there a portable way to do this with NIO? + File myconfFile = myconf.toFile(); + myconfFile.setWritable(false, false /* all */); + myconfFile.setReadable(false, false /* all */); + myconfFile.setExecutable(false, false /* all */); - myconf.setWritable(true, true /* owner only */); - myconf.setReadable(true, true /* owner only */); + myconfFile.setWritable(true, true /* owner only */); + myconfFile.setReadable(true, true /* owner only */); - myconf.deleteOnExit(); + myconfFile.deleteOnExit(); _env.set("GIT_DIR", "."); - _env.set("GITWEB_CONFIG", myconf.getAbsolutePath()); + _env.set("GITWEB_CONFIG", myconf.toAbsolutePath().toString()); - final PrintWriter p = new PrintWriter(new FileWriter(myconf)); - try { + try (PrintWriter p = + new PrintWriter(Files.newBufferedWriter(myconf, UTF_8))) { p.print("# Autogenerated by Gerrit Code Review \n"); p.print("# DO NOT EDIT\n"); p.print("\n"); @@ -174,12 +179,12 @@ // We are mounted at the same level in the context as the main // UI, so we can include the same header and footer scheme. // - final File hdr = site.site_header; - if (hdr.isFile()) { + Path hdr = site.site_header; + if (Files.isRegularFile(hdr)) { p.print("$site_header = " + quoteForPerl(hdr) + ";\n"); } - final File ftr = site.site_footer; - if (ftr.isFile()) { + Path ftr = site.site_footer; + if (Files.isRegularFile(ftr)) { p.print("$site_footer = " + quoteForPerl(ftr) + ";\n"); } @@ -192,8 +197,8 @@ p.print("$logo = 'gitweb-logo.png';\n"); p.print("$javascript = 'gitweb.js';\n"); p.print("@stylesheets = ('gitweb-default.css');\n"); - final File css = site.site_css; - if (css.isFile()) { + Path css = site.site_css; + if (Files.isRegularFile(css)) { p.print("push @stylesheets, 'gitweb-site.css';\n"); } @@ -294,15 +299,15 @@ // If the administrator has created a site-specific gitweb_config, // load that before we perform any final overrides. // - final File sitecfg = site.site_gitweb; - if (sitecfg.isFile()) { + Path sitecfg = site.site_gitweb; + if (Files.isRegularFile(sitecfg)) { p.print("$GITWEB_CONFIG = " + quoteForPerl(sitecfg) + ";\n"); p.print("if (-e $GITWEB_CONFIG) {\n"); p.print(" do " + quoteForPerl(sitecfg) + ";\n"); p.print("}\n"); } - final File root = repoManager.getBasePath(); + Path root = repoManager.getBasePath(); p.print("$projectroot = " + quoteForPerl(root) + ";\n"); // Permit exporting only the project we were started for. @@ -326,18 +331,16 @@ // p.print("$feature{'forks'}{'override'} = 0;\n"); p.print("$feature{'forks'}{'default'} = [0];\n"); - } finally { - p.close(); } - myconf.setReadOnly(); + myconfFile.setReadOnly(); } - private String quoteForPerl(File value) { - return quoteForPerl(value.getAbsolutePath()); + private static String quoteForPerl(Path value) { + return quoteForPerl(value.toAbsolutePath().toString()); } - private String quoteForPerl(String value) { + private static String quoteForPerl(String value) { if (value == null || value.isEmpty()) { return "''"; } @@ -455,9 +458,10 @@ private void exec(final HttpServletRequest req, final HttpServletResponse rsp, final ProjectControl project) throws IOException { final Process proc = - Runtime.getRuntime().exec(new String[] {gitwebCgi.getAbsolutePath()}, + Runtime.getRuntime().exec( + new String[] {gitwebCgi.toAbsolutePath().toString()}, makeEnv(req, project), - gitwebCgi.getAbsoluteFile().getParentFile()); + gitwebCgi.toAbsolutePath().getParent().toFile()); copyStderrToLog(proc.getErrorStream()); if (0 < req.getContentLength()) { @@ -535,7 +539,7 @@ // env.set("REQUEST_METHOD", req.getMethod()); env.set("SCRIPT_NAME", req.getContextPath() + req.getServletPath()); - env.set("SCRIPT_FILENAME", gitwebCgi.getAbsolutePath()); + env.set("SCRIPT_FILENAME", gitwebCgi.toAbsolutePath().toString()); env.set("SERVER_NAME", req.getServerName()); env.set("SERVER_PORT", Integer.toString(req.getServerPort())); env.set("SERVER_PROTOCOL", req.getProtocol());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java index 47ef520..6afe52a 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ContextMapper.java
@@ -45,7 +45,7 @@ return base + name; } - private class WrappedRequest extends HttpServletRequestWrapper { + private static class WrappedRequest extends HttpServletRequestWrapper { private final String contextPath; private final String pathInfo;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java index 64b754d..405a861 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -14,6 +14,7 @@ package com.google.gerrit.httpd.plugins; +import static com.google.gerrit.common.FileUtil.lastModified; import static com.google.gerrit.server.plugins.PluginEntry.ATTR_CHARACTER_ENCODING; import static com.google.gerrit.server.plugins.PluginEntry.ATTR_CONTENT_TYPE; @@ -24,6 +25,7 @@ import com.google.common.cache.Cache; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.io.ByteStreams; import com.google.common.net.HttpHeaders; import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.httpd.resources.Resource; @@ -55,14 +57,14 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -303,7 +305,7 @@ } if (!entry.isPresent() && file.endsWith("/index.html")) { String pfx = file.substring(0, file.length() - "index.html".length()); - long pluginLastModified = holder.plugin.getSrcFile().lastModified(); + long pluginLastModified = lastModified(holder.plugin.getSrcFile()); if (hasUpToDateCachedResource(rsc, pluginLastModified)) { rsc.send(req, res); } else { @@ -611,12 +613,12 @@ private void sendJsPlugin(Plugin plugin, PluginResourceKey key, HttpServletRequest req, HttpServletResponse res) throws IOException { - File pluginFile = plugin.getSrcFile(); + Path path = plugin.getSrcFile(); if (req.getRequestURI().endsWith(getJsPluginPath(plugin)) - && pluginFile.exists()) { - res.setHeader("Content-Length", Long.toString(pluginFile.length())); + && Files.exists(path)) { + res.setHeader("Content-Length", Long.toString(Files.size(path))); res.setContentType("application/javascript"); - writeToResponse(res, new FileInputStream(pluginFile)); + writeToResponse(res, Files.newInputStream(path)); } else { resourceCache.put(key, Resource.NOT_FOUND); Resource.NOT_FOUND.send(req, res); @@ -624,25 +626,15 @@ } private static String getJsPluginPath(Plugin plugin) { - return String.format("/plugins/%s/static/%s", plugin.getName(), plugin.getSrcFile() - .getName()); + return String.format("/plugins/%s/static/%s", plugin.getName(), + plugin.getSrcFile().getFileName()); } - private void writeToResponse(HttpServletResponse res, InputStream in) + private void writeToResponse(HttpServletResponse res, InputStream inputStream) throws IOException { - try { - OutputStream out = res.getOutputStream(); - try { - byte[] tmp = new byte[1024]; - int n; - while ((n = in.read(tmp)) > 0) { - out.write(tmp, 0, n); - } - } finally { - out.close(); - } - } finally { - in.close(); + try (OutputStream out = res.getOutputStream(); + InputStream in = inputStream) { + ByteStreams.copy(in, out); } } @@ -674,9 +666,9 @@ } private static String getPrefix(Plugin plugin, String attr, String def) { - File srcFile = plugin.getSrcFile(); + Path path = plugin.getSrcFile(); PluginContentScanner scanner = plugin.getContentScanner(); - if (srcFile == null || scanner == PluginContentScanner.EMPTY) { + if (path == null || scanner == PluginContentScanner.EMPTY) { return def; } try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java index 90c5ff4..e0d4b51 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.raw; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.hash.Hasher; @@ -30,6 +32,7 @@ import com.google.gerrit.httpd.WebSession; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.notedb.NotesMigration; @@ -47,14 +50,15 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -68,8 +72,8 @@ public class HostPageServlet extends HttpServlet { private static final Logger log = LoggerFactory.getLogger(HostPageServlet.class); - private static final boolean IS_DEV = Boolean.getBoolean("Gerrit.GwtDevMode"); private static final String HPD_ID = "gerrit_hostpagedata"; + private static final int DEFAULT_JS_LOAD_TIMEOUT = 5000; private final Provider<CurrentUser> currentUser; private final DynamicItem<WebSession> session; @@ -84,6 +88,7 @@ private final boolean refreshHeaderFooter; private final StaticServlet staticServlet; private final boolean isNoteDbEnabled; + private final Integer pluginsLoadTimeout; private volatile Page page; @Inject @@ -107,6 +112,7 @@ refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true); staticServlet = ss; isNoteDbEnabled = migration.enabled(); + pluginsLoadTimeout = getPluginsLoadTimeout(cfg); final String pageName = "HostPage.html"; template = HtmlDomUtil.parseFile(getClass(), pageName); @@ -122,54 +128,54 @@ } String src = "gerrit_ui/gerrit_ui.nocache.js"; - if (!IS_DEV) { - Element devmode = HtmlDomUtil.find(template, "gwtdevmode"); - if (devmode != null) { - devmode.getParentNode().removeChild(devmode); - } - - InputStream in = servletContext.getResourceAsStream("/" + src); - if (in != null) { - Hasher md = Hashing.md5().newHasher(); + InputStream in = servletContext.getResourceAsStream("/" + src); + if (in != null) { + Hasher md = Hashing.md5().newHasher(); + try { try { - try { - final byte[] buf = new byte[1024]; - int n; - while ((n = in.read(buf)) > 0) { - md.putBytes(buf, 0, n); - } - } finally { - in.close(); + final byte[] buf = new byte[1024]; + int n; + while ((n = in.read(buf)) > 0) { + md.putBytes(buf, 0, n); } - } catch (IOException e) { - throw new IOException("Failed reading " + src, e); + } finally { + in.close(); } - src += "?content=" + md.hash().toString(); - } else { - log.debug("No " + src + " in webapp root; keeping noncache.js URL"); + } catch (IOException e) { + throw new IOException("Failed reading " + src, e); } + src += "?content=" + md.hash().toString(); + } else { + log.debug("No " + src + " in webapp root; keeping noncache.js URL"); } noCacheName = src; page = new Page(); } + private static int getPluginsLoadTimeout(final Config cfg) { + long cfgValue = + ConfigUtil.getTimeUnit(cfg, "plugins", null, "jsLoadTimeout", + DEFAULT_JS_LOAD_TIMEOUT, TimeUnit.MILLISECONDS); + if (cfgValue < 0) { + return 0; + } + return (int) cfgValue; + } + private void json(final Object data, final StringWriter w) { JsonServlet.defaultGsonBuilder().create().toJson(data, w); } private Page get() { Page p = page; - if (refreshHeaderFooter && p.isStale()) { - final Page newPage; - try { - newPage = new Page(); - } catch (IOException e) { - log.error("Cannot refresh site header/footer", e); - return p; + try { + if (refreshHeaderFooter && p.isStale()) { + p = new Page(); + page = p; } - p = newPage; - page = p; + } catch (IOException e) { + log.error("Cannot refresh site header/footer", e); } return p; } @@ -216,7 +222,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try { @@ -288,16 +294,16 @@ } private static class FileInfo { - private final File path; + private final Path path; private final long time; - FileInfo(final File p) { + FileInfo(Path p) { path = p; - time = path.lastModified(); + time = lastModified(path); } boolean isStale() { - return time != path.lastModified(); + return time != lastModified(path); } } @@ -319,6 +325,7 @@ pageData.version = Version.getVersion(); pageData.config = config; pageData.isNoteDbEnabled = isNoteDbEnabled; + pageData.pluginsLoadTimeout = pluginsLoadTimeout; final StringWriter w = new StringWriter(); w.write("var " + HPD_ID + "="); @@ -364,8 +371,8 @@ } } - private FileInfo injectCssFile(final Document hostDoc, final String id, - final File src) throws IOException { + private FileInfo injectCssFile(Document hostDoc, String id, Path src) + throws IOException { final FileInfo info = new FileInfo(src); final Element banner = HtmlDomUtil.find(hostDoc, id); if (banner == null) { @@ -376,7 +383,8 @@ banner.removeChild(banner.getFirstChild()); } - String css = HtmlDomUtil.readFile(src.getParentFile(), src.getName()); + String css = + HtmlDomUtil.readFile(src.getParent(), src.getFileName().toString()); if (css == null) { return info; } @@ -385,8 +393,8 @@ return info; } - private FileInfo injectXmlFile(final Document hostDoc, final String id, - final File src) throws IOException { + private FileInfo injectXmlFile(Document hostDoc, String id, Path src) + throws IOException { final FileInfo info = new FileInfo(src); final Element banner = HtmlDomUtil.find(hostDoc, id); if (banner == null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java index 9c267a8..00568f0 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
@@ -68,7 +68,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java index 1d8e74d..2f5bc3a 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java
@@ -14,6 +14,9 @@ package com.google.gerrit.httpd.raw; +import static java.nio.file.Files.exists; +import static java.nio.file.Files.isReadable; + import com.google.common.io.ByteStreams; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; @@ -24,11 +27,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -58,13 +61,13 @@ private static final Logger log = LoggerFactory.getLogger(RobotsServlet.class); - private final File robotsFile; + private final Path robotsFile; @Inject RobotsServlet(@GerritServerConfig final Config config, final SitePaths sitePaths) { - File file = sitePaths.resolve( + Path file = sitePaths.resolve( config.getString("httpd", null, "robotsFile")); - if (file != null && (!file.exists() || !file.canRead())) { + if (file != null && (!exists(file) || !isReadable(file))) { log.warn("Cannot read httpd.robotsFile, using default"); file = null; } @@ -75,23 +78,16 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse rsp) throws IOException { rsp.setContentType("text/plain"); - InputStream in = openRobotsFile(); - try { - OutputStream out = rsp.getOutputStream(); - try { - ByteStreams.copy(in, out); - } finally { - out.close(); - } - } finally { - in.close(); + try (InputStream in = openRobotsFile(); + OutputStream out = rsp.getOutputStream()) { + ByteStreams.copy(in, out); } } private InputStream openRobotsFile() { if (robotsFile != null) { try { - return new FileInputStream(robotsFile); + return Files.newInputStream(robotsFile); } catch (IOException e) { log.warn("Cannot read " + robotsFile + "; using default", e); }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java index 83120e0..b85cdf0 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
@@ -40,12 +40,14 @@ * <p> * Versions of Git before 1.5.3 may require setting the username and port * properties in the user's {@code ~/.ssh/config} file, and using a host - * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git: + * alias through a URL such as {@code gerrit-alias:/tools/gerrit.git}: * <pre> + * {@code * Host gerrit-alias * User sop@google.com * Hostname gerrit.com * Port 8010 + * } * </pre> */ @SuppressWarnings("serial")
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java index 52b7a5c9..e690334 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java
@@ -17,6 +17,7 @@ import static com.google.common.net.HttpHeaders.CONTENT_ENCODING; import static com.google.common.net.HttpHeaders.ETAG; import static com.google.common.net.HttpHeaders.IF_NONE_MATCH; +import static com.google.gerrit.common.FileUtil.lastModified; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.MINUTES; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; @@ -30,7 +31,7 @@ import com.google.common.cache.Weigher; import com.google.common.collect.Maps; import com.google.common.hash.Hashing; -import com.google.common.io.ByteStreams; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.common.Nullable; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -44,11 +45,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -89,21 +90,19 @@ return type != null ? type : "application/octet-stream"; } - private final File staticBase; - private final String staticBasePath; + private final Path staticBase; private final boolean refresh; private final LoadingCache<String, Resource> cache; @Inject StaticServlet(@GerritServerConfig Config cfg, SitePaths site) { - File f; + Path p; try { - f = site.static_dir.getCanonicalFile(); + p = site.static_dir.toRealPath().normalize(); } catch (IOException e) { - f = site.static_dir.getAbsoluteFile(); + p = site.static_dir.toAbsolutePath().normalize(); } - staticBase = f; - staticBasePath = staticBase.getPath() + File.separator; + staticBase = p; refresh = cfg.getBoolean("site", "refreshHeaderFooter", true); cache = CacheBuilder.newBuilder() .maximumWeight(1 << 20) @@ -131,7 +130,8 @@ } } - private Resource getResource(HttpServletRequest req) throws ExecutionException { + private Resource getResource(HttpServletRequest req) + throws ExecutionException { String name = CharMatcher.is('/').trimFrom(req.getPathInfo()); if (isUnreasonableName(name)) { return Resource.NOT_FOUND; @@ -150,13 +150,12 @@ } private static boolean isUnreasonableName(String name) { - if (name.length() < 1) return true; - if (name.contains("\\")) return true; // no windows/dos style paths - if (name.startsWith("../")) return true; // no "../etc/passwd" - if (name.contains("/../")) return true; // no "foo/../etc/passwd" - if (name.contains("/./")) return true; // "foo/./foo" is insane to ask - if (name.contains("//")) return true; // windows UNC path can be "//..." - return false; // is a reasonable name + return name.length() < 1 + || name.contains("\\") // no windows/dos style paths + || name.startsWith("../") // no "../etc/passwd" + || name.contains("/../") // no "foo/../etc/passwd" + || name.contains("/./") // "foo/./foo" is insane to ask + || name.contains("//"); // windows UNC path can be "//..." } @Override @@ -209,29 +208,22 @@ } private Resource loadResource(String name) throws IOException { - File p = new File(staticBase, name); + Path p = staticBase.resolve(name); try { - p = p.getCanonicalFile(); + p = p.toRealPath().normalize(); } catch (IOException e) { return Resource.NOT_FOUND; } - if (!p.getPath().startsWith(staticBasePath)) { + if (!p.startsWith(staticBase)) { return Resource.NOT_FOUND; } - long ts = p.lastModified(); - FileInputStream in; - try { - in = new FileInputStream(p); - } catch (FileNotFoundException e) { - return Resource.NOT_FOUND; - } - + long ts = FileUtil.lastModified(p); byte[] raw; try { - raw = ByteStreams.toByteArray(in); - } finally { - in.close(); + raw = Files.readAllBytes(p); + } catch (NoSuchFileException e) { + return Resource.NOT_FOUND; } return new Resource(p, ts, contentType(name), raw); } @@ -239,13 +231,13 @@ static class Resource { static final Resource NOT_FOUND = new Resource(null, -1, "", new byte[] {}); - final File src; + final Path src; final long lastModified; final String contentType; final String etag; final byte[] raw; - Resource(File src, long lastModified, String contentType, byte[] raw) { + Resource(Path src, long lastModified, String contentType, byte[] raw) { this.src = src; this.lastModified = lastModified; this.contentType = contentType; @@ -254,7 +246,7 @@ } boolean isStale() { - return lastModified != src.lastModified(); + return lastModified != lastModified(src); } } }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java index 6f4cc08..0045649 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -46,7 +46,7 @@ import com.google.common.math.IntMath; import com.google.common.net.HttpHeaders; import com.google.gerrit.audit.AuditService; -import com.google.gerrit.audit.HttpAuditEvent; +import com.google.gerrit.audit.ExtendedHttpAuditEvent; import com.google.gerrit.common.Nullable; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.extensions.registration.DynamicItem; @@ -142,6 +142,8 @@ private static final String JSON_TYPE = "application/json"; private static final String FORM_TYPE = "application/x-www-form-urlencoded"; + private static final int HEAP_EST_SIZE = 10 * 8 * 1024; // Presize 10 blocks. + /** * Garbage prefix inserted before JSON output to prevent XSSI. * <p> @@ -202,6 +204,8 @@ Object result = null; Multimap<String, String> params = LinkedHashMultimap.create(); Object inputRequestBody = null; + RestResource rsrc = TopLevelResource.INSTANCE; + ViewData viewData = null; try { checkUserSession(req); @@ -211,8 +215,8 @@ CapabilityUtils.checkRequiresCapability(globals.currentUser, null, rc.getClass()); - RestResource rsrc = TopLevelResource.INSTANCE; - ViewData viewData = new ViewData(null, null); + viewData = new ViewData(null, null); + if (path.isEmpty()) { if (isGetOrHead(req)) { viewData = new ViewData(null, rc.list()); @@ -384,10 +388,10 @@ status = SC_INTERNAL_SERVER_ERROR; handleException(e, req, res); } finally { - globals.auditService.dispatch(new HttpAuditEvent(globals.webSession.get() - .getSessionId(), globals.currentUser.get(), req.getRequestURI(), - auditStartTs, params, req.getMethod(), inputRequestBody, status, - result)); + globals.auditService.dispatch(new ExtendedHttpAuditEvent(globals.webSession.get() + .getSessionId(), globals.currentUser.get(), req, + auditStartTs, params, inputRequestBody, status, + result, rsrc, viewData == null ? null : viewData.view)); } } @@ -656,7 +660,7 @@ Multimap<String, String> config, Object result) throws IOException { - TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE); + TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, Integer.MAX_VALUE); buf.write(JSON_MAGIC); Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8)); Gson gson = newGson(config, req); @@ -781,7 +785,7 @@ private static BinaryResult stackJsonString(HttpServletResponse res, final BinaryResult src) throws IOException { - TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE); + TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, Integer.MAX_VALUE); buf.write(JSON_MAGIC); try(Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8)); JsonWriter json = new JsonWriter(w)) { @@ -958,7 +962,8 @@ if (user instanceof AnonymousUser) { throw new AuthException("Authentication required"); } else if (!globals.webSession.get().isAccessPathOk(AccessPath.REST_API)) { - throw new AuthException("Invalid authentication method. In order to authenticate, prefix the REST endpoint URL with /a/ (e.g. http://example.com/a/projects/)."); + throw new AuthException("Invalid authentication method. In order to authenticate, " + + "prefix the REST endpoint URL with /a/ (e.g. http://example.com/a/projects/)."); } } user.setAccessPath(AccessPath.REST_API); @@ -1053,10 +1058,15 @@ return false; } + private static int base64MaxSize(long n) { + return 4 * IntMath.divide((int) n, 3, CEILING); + } + private static BinaryResult base64(BinaryResult bin) throws IOException { - int max = 4 * IntMath.divide((int) bin.getContentLength(), 3, CEILING); - TemporaryBuffer.Heap buf = heap(max); + int maxSize = base64MaxSize(bin.getContentLength()); + int estSize = Math.min(base64MaxSize(HEAP_EST_SIZE), maxSize); + TemporaryBuffer.Heap buf = heap(estSize, maxSize); OutputStream encoded = BaseEncoding.base64().encodingStream( new OutputStreamWriter(buf, ISO_8859_1)); bin.writeTo(encoded); @@ -1066,7 +1076,7 @@ private static BinaryResult compress(BinaryResult bin) throws IOException { - TemporaryBuffer.Heap buf = heap(20 << 20); + TemporaryBuffer.Heap buf = heap(HEAP_EST_SIZE, 20 << 20); GZIPOutputStream gz = new GZIPOutputStream(buf); bin.writeTo(gz); gz.close(); @@ -1083,8 +1093,8 @@ }.setContentLength(buf.length()); } - private static Heap heap(int max) { - return new TemporaryBuffer.Heap(max); + private static Heap heap(int est, int max) { + return new TemporaryBuffer.Heap(est, max); } @SuppressWarnings("serial")
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java index e1c9e3c..a12d8d5 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
@@ -201,8 +201,9 @@ public VoidResult run(final ReviewDb db) throws OrmException, Failure { final Account.Id me = getAccountId(); for (final AccountProjectWatch.Key keyId : keys) { - if (!me.equals(keyId.getParentKey())) + if (!me.equals(keyId.getParentKey())) { throw new Failure(new NoSuchEntityException()); + } } db.accountProjectWatches().deleteKeys(keys);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java index 9437bbe..dff2cd0 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -14,13 +14,16 @@ package com.google.gerrit.httpd.rpc.project; +import com.google.gerrit.common.FooterConstants; import com.google.gerrit.common.Nullable; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.extensions.api.changes.AddReviewerInput; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; @@ -100,6 +103,7 @@ protected Change.Id updateProjectConfig(ProjectControl ctl, ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate) throws IOException, OrmException { + md.setInsertChangeId(true); Change.Id changeId = new Change.Id(db.nextChangeId()); RevCommit commit = config.commitToNewRef(md, new PatchSet.Id(changeId, @@ -109,7 +113,7 @@ } Change change = new Change( - new Change.Key("I" + commit.name()), + getChangeId(commit), changeId, user.getAccountId(), new Branch.NameKey( @@ -133,6 +137,14 @@ return changeId; } + private static Change.Key getChangeId(RevCommit commit) { + List<String> idList = commit.getFooterLines(FooterConstants.CHANGE_ID); + Change.Key changeKey = !idList.isEmpty() + ? new Change.Key(idList.get(idList.size() - 1).trim()) + : new Change.Key("I" + commit.name()); + return changeKey; + } + private void addProjectOwnersAsReviewers(ChangeResource rsrc) { final String projectOwners = groupBackend.get(SystemGroupBackend.PROJECT_OWNERS).getName(); @@ -140,7 +152,7 @@ AddReviewerInput input = new AddReviewerInput(); input.reviewer = projectOwners; reviewersProvider.get().apply(rsrc, input); - } catch (Exception e) { + } catch (IOException | OrmException | RestApiException | EmailException e) { // one of the owner groups is not visible to the user and this it why it // can't be added as reviewer } @@ -156,7 +168,7 @@ AddReviewerInput input = new AddReviewerInput(); input.reviewer = r.getGroup().getUUID().get(); reviewersProvider.get().apply(rsrc, input); - } catch (Exception e) { + } catch (IOException | OrmException | RestApiException | EmailException e) { // ignore } }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java index 321f032..9c067de 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.template; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -27,8 +29,8 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; @Singleton public class SiteHeaderFooter { @@ -43,13 +45,13 @@ this.refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true); this.sitePaths = sitePaths; - Template t = new Template(sitePaths); try { + Template t = new Template(sitePaths); t.load(); + template = t; } catch (IOException e) { log.warn("Cannot load site header or footer", e); } - template = t; } public Document parse(Class<?> clazz, String name) throws IOException { @@ -118,8 +120,8 @@ void load() throws IOException { css = HtmlDomUtil.readFile( - cssFile.path.getParentFile(), - cssFile.path.getName()); + cssFile.path.getParent(), + cssFile.path.getFileName().toString()); header = readXml(headerFile); footer = readXml(footerFile); } @@ -135,16 +137,16 @@ } private static class FileInfo { - final File path; + final Path path; final long time; - FileInfo(File p) { + FileInfo(Path p) { path = p; - time = path.lastModified(); + time = lastModified(p); } boolean isStale() { - return time != path.lastModified(); + return time != lastModified(path); } } }
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html index c660311..23d5856 100644 --- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html +++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
@@ -1,34 +1,6 @@ <html> <head> <title>Gerrit Code Review</title> - <script id="gwtdevmode"> - (function () { - var pn = 'gwt.codesvr'; - var cn = 'gerrit_ui.' + pn; - - var p_start = window.location.search.indexOf(pn + '='); - if (p_start != -1) { - p_start = p_start + pn.length + 1; - var p_end = window.location.search.indexOf(";", p_start); - if (p_end == -1) p_end = window.location.search.length; - var v = window.location.search.substring(p_start, p_end); - - var e = new Date(); - e.setDate(e.getDate() + 1); - document.cookie = cn + "=" + v + ';expires=' + e.toGMTString(); - - } else if (document.cookie.length != 0) { - var c_start = document.cookie.indexOf(cn + '='); - if (c_start != -1) { - c_start = c_start + cn.length + 1; - var c_end = document.cookie.indexOf(";", c_start); - if (c_end == -1) c_end = document.cookie.length; - var v = document.cookie.substring(c_start, c_end); - window.location.replace('?' + pn + '=' + v + document.location.hash); - } - } - })(); - </script> <style id="gerrit_sitecss" type="text/css"></style> </head> <body>
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html index 9f3fa1e..d2a333e 100644 --- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html +++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html
@@ -2,34 +2,6 @@ <head> <title>Gerrit Code Review</title> <meta name="gwt:property" content="locale=en_US" /> - <script id="gwtdevmode"> - (function () { - var pn = 'gwt.codesvr'; - var cn = 'gerrit_ui.' + pn; - - var p_start = window.location.search.indexOf(pn + '='); - if (p_start != -1) { - p_start = p_start + pn.length + 1; - var p_end = window.location.search.indexOf(";", p_start); - if (p_end == -1) p_end = window.location.search.length; - var v = window.location.search.substring(p_start, p_end); - - var e = new Date(); - e.setDate(e.getDate() + 1); - document.cookie = cn + "=" + v + ';expires=' + e.toGMTString(); - - } else if (document.cookie.length != 0) { - var c_start = document.cookie.indexOf(cn + '='); - if (c_start != -1) { - c_start = c_start + cn.length + 1; - var c_end = document.cookie.indexOf(";", c_start); - if (c_end == -1) c_end = document.cookie.length; - var v = document.cookie.substring(c_start, c_end); - window.location.replace('?' + pn + '=' + v + document.location.hash); - } - } - })(); - </script> <script id="gerrit_hostpagedata"></script> <style id="gerrit_sitecss" type="text/css"></style> <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java index 4ee9676..e3e6d48 100644 --- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java +++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -46,11 +46,11 @@ private static final String pkg = "com.google.gerrit.pgm"; public static final String NOT_ARCHIVED = "NOT_ARCHIVED"; - public static void main(final String argv[]) throws Exception { + public static void main(final String[] argv) throws Exception { System.exit(mainImpl(argv)); } - public static int mainImpl(final String argv[]) throws Exception { + public static int mainImpl(final String[] argv) throws Exception { if (argv.length == 0) { File me; try { @@ -88,11 +88,15 @@ // Run the application class // - final ClassLoader cl = libClassLoader(); + final ClassLoader cl = libClassLoader(isProlog(programClassName(argv[0]))); Thread.currentThread().setContextClassLoader(cl); return invokeProgram(cl, argv); } + private static boolean isProlog(String cn) { + return "PrologShell".equals(cn) || "Rulec".equals(cn); + } + private static String getVersion(final File me) { if (me == null) { return ""; @@ -122,20 +126,7 @@ Class<?> clazz; try { try { - String cn = name; - if (cn.equals(cn.toLowerCase())) { - StringBuilder buf = new StringBuilder(); - buf.append(Character.toUpperCase(cn.charAt(0))); - for (int i = 1; i < cn.length(); i++) { - if (cn.charAt(i) == '-' && i + 1 < cn.length()) { - i++; - buf.append(Character.toUpperCase(cn.charAt(i))); - } else { - buf.append(cn.charAt(i)); - } - } - cn = buf.toString(); - } + String cn = programClassName(name); clazz = Class.forName(pkg + "." + cn, true, loader); } catch (ClassNotFoundException cnfe) { if (name.equals(name.toLowerCase())) { @@ -181,7 +172,25 @@ } } - private static ClassLoader libClassLoader() throws IOException { + private static String programClassName(String cn) { + if (cn.equals(cn.toLowerCase())) { + StringBuilder buf = new StringBuilder(); + buf.append(Character.toUpperCase(cn.charAt(0))); + for (int i = 1; i < cn.length(); i++) { + if (cn.charAt(i) == '-' && i + 1 < cn.length()) { + i++; + buf.append(Character.toUpperCase(cn.charAt(i))); + } else { + buf.append(cn.charAt(i)); + } + } + return buf.toString(); + } + return cn; + } + + private static ClassLoader libClassLoader(boolean prologCompiler) + throws IOException { final File path; try { path = getDistributionArchive(); @@ -201,10 +210,16 @@ final ZipEntry ze = e.nextElement(); if (ze.isDirectory()) { continue; - } else if (ze.getName().startsWith("WEB-INF/lib/")) { + } + + String name = ze.getName(); + if (name.startsWith("WEB-INF/lib/")) { extractJar(zf, ze, jars); - } else if (ze.getName().startsWith("WEB-INF/pgm-lib/")) { - extractJar(zf, ze, jars); + } else if (name.startsWith("WEB-INF/pgm-lib/")) { + // Some Prolog tools are restricted. + if (prologCompiler || !name.startsWith("WEB-INF/pgm-lib/prolog-")) { + extractJar(zf, ze, jars); + } } } } finally {
diff --git a/gerrit-lucene/BUCK b/gerrit-lucene/BUCK index 2b45d2b..a146774 100644 --- a/gerrit-lucene/BUCK +++ b/gerrit-lucene/BUCK
@@ -34,7 +34,9 @@ '//lib/jgit:jgit', '//lib/log:api', '//lib/lucene:analyzers-common', + '//lib/lucene:backward-codecs', '//lib/lucene:core', + '//lib/lucene:misc', ], visibility = ['PUBLIC'], )
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java index e0c13ae..27ded17 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java
@@ -14,7 +14,6 @@ package com.google.gerrit.lucene; -import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; @@ -43,13 +42,6 @@ } @Override - public void addDocument(Iterable<? extends IndexableField> doc, - Analyzer analyzer) throws IOException { - super.addDocument(doc, analyzer); - autoFlush(); - } - - @Override public void addDocuments( Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException { @@ -58,14 +50,6 @@ } @Override - public void addDocuments( - Iterable<? extends Iterable<? extends IndexableField>> docs, - Analyzer analyzer) throws IOException { - super.addDocuments(docs, analyzer); - autoFlush(); - } - - @Override public void updateDocuments(Term delTerm, Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException { @@ -74,14 +58,6 @@ } @Override - public void updateDocuments(Term delTerm, - Iterable<? extends Iterable<? extends IndexableField>> docs, - Analyzer analyzer) throws IOException { - super.updateDocuments(delTerm, docs, analyzer); - autoFlush(); - } - - @Override public void deleteDocuments(Term... term) throws IOException { super.deleteDocuments(term); autoFlush(); @@ -111,13 +87,6 @@ } @Override - public void updateDocument(Term term, Iterable<? extends IndexableField> doc, - Analyzer analyzer) throws IOException { - super.updateDocument(term, doc, analyzer); - autoFlush(); - } - - @Override public void deleteAll() throws IOException { super.deleteAll(); autoFlush();
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java index e8825c5..64da702 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -14,7 +14,8 @@ package com.google.gerrit.lucene; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE; import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES; import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES; @@ -30,7 +31,7 @@ import com.google.common.util.concurrent.ListeningExecutorService; import com.google.gerrit.common.Nullable; import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSetApproval; +import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -38,8 +39,8 @@ import com.google.gerrit.server.index.ChangeField; import com.google.gerrit.server.index.ChangeField.ChangeProtoField; import com.google.gerrit.server.index.ChangeField.PatchSetApprovalProtoField; +import com.google.gerrit.server.index.ChangeField.PatchSetProtoField; import com.google.gerrit.server.index.ChangeIndex; -import com.google.gerrit.server.index.ChangeSchemas; import com.google.gerrit.server.index.FieldDef; import com.google.gerrit.server.index.FieldDef.FillArgs; import com.google.gerrit.server.index.FieldType; @@ -51,6 +52,7 @@ import com.google.gerrit.server.query.QueryParseException; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeDataSource; +import com.google.gwtorm.protobuf.ProtobufCodec; import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.ResultSet; import com.google.inject.Provider; @@ -64,9 +66,12 @@ import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.IntField; import org.apache.lucene.document.LongField; +import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig.OpenMode; @@ -76,22 +81,26 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.SearcherFactory; import org.apache.lucene.search.SearcherManager; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TopFieldDocs; import org.apache.lucene.store.RAMDirectory; +import org.apache.lucene.uninverting.UninvertingReader; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.Version; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -119,54 +128,20 @@ private static final String CHANGE_FIELD = ChangeField.CHANGE.getName(); private static final String DELETED_FIELD = ChangeField.DELETED.getName(); private static final String ID_FIELD = ChangeField.LEGACY_ID.getName(); + private static final String ID_SORT_FIELD = + sortFieldName(ChangeField.LEGACY_ID); private static final String MERGEABLE_FIELD = ChangeField.MERGEABLE.getName(); + private static final String PATCH_SET_FIELD = ChangeField.PATCH_SET.getName(); + private static final String UPDATED_SORT_FIELD = + sortFieldName(ChangeField.UPDATED); + private static final ImmutableSet<String> FIELDS = ImmutableSet.of( ADDED_FIELD, APPROVAL_FIELD, CHANGE_FIELD, DELETED_FIELD, ID_FIELD, - MERGEABLE_FIELD); + MERGEABLE_FIELD, PATCH_SET_FIELD); + private static final Map<String, String> CUSTOM_CHAR_MAPPING = ImmutableMap.of( "_", " ", ".", " "); - private static final Map<Schema<ChangeData>, Version> LUCENE_VERSIONS; - static { - ImmutableMap.Builder<Schema<ChangeData>, Version> versions = - ImmutableMap.builder(); - @SuppressWarnings("deprecation") - Version lucene43 = Version.LUCENE_43; - @SuppressWarnings("deprecation") - Version lucene44 = Version.LUCENE_44; - @SuppressWarnings("deprecation") - Version lucene46 = Version.LUCENE_46; - @SuppressWarnings("deprecation") - Version lucene47 = Version.LUCENE_47; - @SuppressWarnings("deprecation") - Version lucene48 = Version.LUCENE_48; - @SuppressWarnings("deprecation") - Version lucene410 = Version.LUCENE_4_10_0; - // We are using 4.10.2 but there is no difference in the index - // format since 4.10.1, so we reuse the version here. - @SuppressWarnings("deprecation") - Version lucene4101 = Version.LUCENE_4_10_1; - for (Map.Entry<Integer, Schema<ChangeData>> e - : ChangeSchemas.ALL.entrySet()) { - if (e.getKey() <= 3) { - versions.put(e.getValue(), lucene43); - } else if (e.getKey() <= 5) { - versions.put(e.getValue(), lucene44); - } else if (e.getKey() <= 8) { - versions.put(e.getValue(), lucene46); - } else if (e.getKey() <= 10) { - versions.put(e.getValue(), lucene47); - } else if (e.getKey() <= 11) { - versions.put(e.getValue(), lucene48); - } else if (e.getKey() <= 13) { - versions.put(e.getValue(), lucene410); - } else { - versions.put(e.getValue(), lucene4101); - } - } - LUCENE_VERSIONS = versions.build(); - } - public static void setReady(SitePaths sitePaths, int version, boolean ready) throws IOException { try { @@ -179,6 +154,10 @@ } } + private static String sortFieldName(FieldDef<?, ?> f) { + return f.getName() + "_SORT"; + } + static interface Factory { LuceneChangeIndex create(Schema<ChangeData> schema, String base); } @@ -187,12 +166,13 @@ private final IndexWriterConfig luceneConfig; private long commitWithinMs; - private GerritIndexWriterConfig(Version version, Config cfg, String name) { + private GerritIndexWriterConfig(Config cfg, String name) { CustomMappingAnalyzer analyzer = new CustomMappingAnalyzer(new StandardAnalyzer( CharArraySet.EMPTY_SET), CUSTOM_CHAR_MAPPING); - luceneConfig = new IndexWriterConfig(version, analyzer); - luceneConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); + luceneConfig = new IndexWriterConfig(analyzer) + .setOpenMode(OpenMode.CREATE_OR_APPEND) + .setCommitOnClose(true); double m = 1 << 20; luceneConfig.setRAMBufferSizeMB(cfg.getLong( "index", name, "ramBufferSize", @@ -223,12 +203,22 @@ private final ListeningExecutorService executor; private final Provider<ReviewDb> db; private final ChangeData.Factory changeDataFactory; - private final File dir; private final Schema<ChangeData> schema; private final QueryBuilder queryBuilder; private final SubIndex openIndex; private final SubIndex closedIndex; + /** + * Whether to use DocValues for range/sorted numeric fields. + * <p> + * Lucene 5 removed support for sorting based on normal numeric fields, so we + * use the newer API for more strongly typed numeric fields in newer schema + * versions. These fields also are not stored, so we need to store auxiliary + * stored-only field for them as well. + */ + // TODO(dborowitz): Delete when we delete support for pre-Lucene-5.0 schemas. + private final boolean useDocValuesForSorting; + @AssistedInject LuceneChangeIndex( @GerritServerConfig Config cfg, @@ -245,15 +235,8 @@ this.db = db; this.changeDataFactory = changeDataFactory; this.schema = schema; + this.useDocValuesForSorting = schema.getVersion() >= 15; - if (base == null) { - dir = LuceneVersionManager.getDir(sitePaths, schema); - } else { - dir = new File(base); - } - Version luceneVersion = checkNotNull( - LUCENE_VERSIONS.get(schema), - "unknown Lucene version for index schema: %s", schema); CustomMappingAnalyzer analyzer = new CustomMappingAnalyzer(new StandardAnalyzer(CharArraySet.EMPTY_SET), CUSTOM_CHAR_MAPPING); @@ -263,19 +246,44 @@ BooleanQuery.getMaxClauseCount())); GerritIndexWriterConfig openConfig = - new GerritIndexWriterConfig(luceneVersion, cfg, "changes_open"); + new GerritIndexWriterConfig(cfg, "changes_open"); GerritIndexWriterConfig closedConfig = - new GerritIndexWriterConfig(luceneVersion, cfg, "changes_closed"); + new GerritIndexWriterConfig(cfg, "changes_closed"); + SearcherFactory searcherFactory = newSearcherFactory(); if (cfg.getBoolean("index", "lucene", "testInmemory", false)) { - openIndex = new SubIndex(new RAMDirectory(), "ramOpen", openConfig); - closedIndex = new SubIndex(new RAMDirectory(), "ramClosed", closedConfig); + openIndex = new SubIndex(new RAMDirectory(), "ramOpen", openConfig, + searcherFactory); + closedIndex = new SubIndex(new RAMDirectory(), "ramClosed", closedConfig, + searcherFactory); } else { - openIndex = new SubIndex(new File(dir, CHANGES_OPEN), openConfig); - closedIndex = new SubIndex(new File(dir, CHANGES_CLOSED), closedConfig); + Path dir = base != null ? Paths.get(base) + : LuceneVersionManager.getDir(sitePaths, schema); + openIndex = new SubIndex(dir.resolve(CHANGES_OPEN), openConfig, + searcherFactory); + closedIndex = new SubIndex(dir.resolve(CHANGES_CLOSED), closedConfig, + searcherFactory); } } + private SearcherFactory newSearcherFactory() { + if (useDocValuesForSorting) { + return new SearcherFactory(); + } + final Map<String, UninvertingReader.Type> mapping = ImmutableMap.of( + ChangeField.LEGACY_ID.getName(), UninvertingReader.Type.INTEGER, + ChangeField.UPDATED.getName(), UninvertingReader.Type.LONG); + return new SearcherFactory() { + @Override + public IndexSearcher newSearcher(IndexReader reader) throws IOException { + checkState(reader instanceof DirectoryReader, + "expected DirectoryReader, found %s", reader.getClass().getName()); + return new IndexSearcher( + UninvertingReader.wrap((DirectoryReader) reader, mapping)); + } + }; + } + @Override public void close() { List<ListenableFuture<?>> closeFutures = Lists.newArrayListWithCapacity(2); @@ -358,12 +366,18 @@ setReady(sitePaths, schema.getVersion(), ready); } - private static Sort getSort() { - return new Sort( - new SortField( - ChangeField.UPDATED.getName(), SortField.Type.LONG, true), - new SortField( - ChangeField.LEGACY_ID.getName(), SortField.Type.INT, true)); + private Sort getSort() { + if (useDocValuesForSorting) { + return new Sort( + new SortField(UPDATED_SORT_FIELD, SortField.Type.LONG, true), + new SortField(ID_SORT_FIELD, SortField.Type.LONG, true)); + } else { + return new Sort( + new SortField( + ChangeField.UPDATED.getName(), SortField.Type.LONG, true), + new SortField( + ChangeField.LEGACY_ID.getName(), SortField.Type.INT, true)); + } } private class QuerySource implements ChangeDataSource { @@ -402,7 +416,7 @@ IndexSearcher[] searchers = new IndexSearcher[indexes.size()]; try { int realLimit = start + limit; - TopDocs[] hits = new TopDocs[indexes.size()]; + TopFieldDocs[] hits = new TopFieldDocs[indexes.size()]; for (int i = 0; i < indexes.size(); i++) { searchers[i] = indexes.get(i).acquire(); hits[i] = searchers[i].search(query, realLimit, sort); @@ -462,18 +476,19 @@ cb.bytes, cb.offset, cb.length); ChangeData cd = changeDataFactory.create(db.get(), change); - // Approvals. - BytesRef[] approvalsBytes = doc.getBinaryValues(APPROVAL_FIELD); - if (approvalsBytes != null) { - List<PatchSetApproval> approvals = - Lists.newArrayListWithCapacity(approvalsBytes.length); - for (BytesRef ab : approvalsBytes) { - approvals.add(PatchSetApprovalProtoField.CODEC.decode( - ab.bytes, ab.offset, ab.length)); - } - cd.setCurrentApprovals(approvals); + // Patch sets. + List<PatchSet> patchSets = + decodeProtos(doc, PATCH_SET_FIELD, PatchSetProtoField.CODEC); + if (!patchSets.isEmpty()) { + // Will be an empty list for schemas prior to when this field was stored; + // this cannot be valid since a change needs at least one patch set. + cd.setPatchSets(patchSets); } + // Approvals. + cd.setCurrentApprovals( + decodeProtos(doc, APPROVAL_FIELD, PatchSetApprovalProtoField.CODEC)); + // Changed lines. IndexableField added = doc.getField(ADDED_FIELD); IndexableField deleted = doc.getField(DELETED_FIELD); @@ -494,6 +509,19 @@ return cd; } + private static <T> List<T> decodeProtos(Document doc, String fieldName, + ProtobufCodec<T> codec) { + BytesRef[] bytesRefs = doc.getBinaryValues(fieldName); + if (bytesRefs.length == 0) { + return Collections.emptyList(); + } + List<T> result = new ArrayList<>(bytesRefs.length); + for (BytesRef r : bytesRefs) { + result.add(codec.decode(r.bytes, r.offset, r.length)); + } + return result; + } + private Document toDocument(ChangeData cd) { Document result = new Document(); for (Values<ChangeData> vs : schema.buildFields(cd, fillArgs)) { @@ -509,6 +537,16 @@ FieldType<?> type = values.getField().getType(); Store store = store(values.getField()); + if (useDocValuesForSorting) { + if (values.getField() == ChangeField.LEGACY_ID) { + int v = (Integer) getOnlyElement(values.getValues()); + doc.add(new NumericDocValuesField(ID_SORT_FIELD, v)); + } else if (values.getField() == ChangeField.UPDATED) { + long t = ((Timestamp) getOnlyElement(values.getValues())).getTime(); + doc.add(new NumericDocValuesField(UPDATED_SORT_FIELD, t)); + } + } + if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) { for (Object value : values.getValues()) { doc.add(new IntField(name, (Integer) value, store)); @@ -535,7 +573,7 @@ doc.add(new StoredField(name, (byte[]) value)); } } else { - throw QueryBuilder.badFieldType(type); + throw FieldType.badFieldType(type); } }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java index 672a7c9..35d6636 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneIndexModule.java
@@ -16,6 +16,7 @@ import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleModule; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.index.ChangeSchemas; import com.google.gerrit.server.index.IndexCollection; import com.google.gerrit.server.index.IndexConfig; @@ -26,6 +27,8 @@ import com.google.inject.Provides; import com.google.inject.Singleton; +import org.eclipse.jgit.lib.Config; + public class LuceneIndexModule extends LifecycleModule { private final Integer singleVersion; private final int threads; @@ -44,7 +47,6 @@ @Override protected void configure() { - bind(IndexConfig.class).toInstance(IndexConfig.createDefault()); factory(LuceneChangeIndex.Factory.class); install(new IndexModule(threads)); if (singleVersion == null && base == null) { @@ -54,7 +56,13 @@ } } - private class MultiVersionModule extends LifecycleModule { + @Provides + @Singleton + IndexConfig getIndexConfig(@GerritServerConfig Config cfg) { + return IndexConfig.fromConfig(cfg); + } + + private static class MultiVersionModule extends LifecycleModule { @Override public void configure() { factory(OnlineReindexer.Factory.class);
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java index c3570a1..109525a 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -20,6 +20,7 @@ import com.google.common.collect.Maps; import com.google.common.primitives.Ints; import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.index.ChangeSchemas; import com.google.gerrit.server.index.IndexCollection; @@ -36,8 +37,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.TreeMap; @@ -65,15 +68,16 @@ } } - static File getDir(SitePaths sitePaths, Schema<ChangeData> schema) { - return new File(sitePaths.index_dir, String.format("%s%04d", + static Path getDir(SitePaths sitePaths, Schema<ChangeData> schema) { + return sitePaths.index_dir.resolve(String.format("%s%04d", CHANGES_PREFIX, schema.getVersion())); } static FileBasedConfig loadGerritIndexConfig(SitePaths sitePaths) throws ConfigInvalidException, IOException { FileBasedConfig cfg = new FileBasedConfig( - new File(sitePaths.index_dir, "gerrit_index.config"), FS.detect()); + sitePaths.index_dir.resolve("gerrit_index.config").toFile(), + FS.detect()); cfg.load(); return cfg; } @@ -90,9 +94,11 @@ private final LuceneChangeIndex.Factory indexFactory; private final IndexCollection indexes; private final OnlineReindexer.Factory reindexerFactory; + private final boolean onlineUpgrade; @Inject LuceneVersionManager( + @GerritServerConfig Config cfg, SitePaths sitePaths, LuceneChangeIndex.Factory indexFactory, IndexCollection indexes, @@ -101,6 +107,7 @@ this.indexFactory = indexFactory; this.indexes = indexes; this.reindexerFactory = reindexerFactory; + this.onlineUpgrade = cfg.getBoolean("index", null, "onlineUpgrade", true); } @Override @@ -114,10 +121,10 @@ throw fail(e); } - if (!sitePaths.index_dir.exists()) { + if (!Files.exists(sitePaths.index_dir)) { throw new ProvisionException("No index versions ready; run Reindex"); - } else if (!sitePaths.index_dir.isDirectory()) { - log.warn("Not a directory: %s", sitePaths.index_dir.getAbsolutePath()); + } else if (!Files.exists(sitePaths.index_dir)) { + log.warn("Not a directory: %s", sitePaths.index_dir.toAbsolutePath()); throw new ProvisionException("No index versions ready; run Reindex"); } @@ -130,7 +137,7 @@ if (v.schema == null) { continue; } - if (write.isEmpty()) { + if (write.isEmpty() && onlineUpgrade) { write.add(v); } if (v.ready) { @@ -159,7 +166,7 @@ } int latest = write.get(0).version; - if (latest != search.version) { + if (onlineUpgrade && latest != search.version) { reindexerFactory.create(latest).start(); } } @@ -167,29 +174,35 @@ private TreeMap<Integer, Version> scanVersions(Config cfg) { TreeMap<Integer, Version> versions = Maps.newTreeMap(); for (Schema<ChangeData> schema : ChangeSchemas.ALL.values()) { - File f = getDir(sitePaths, schema); - boolean exists = f.exists() && f.isDirectory(); - if (f.exists() && !f.isDirectory()) { - log.warn("Not a directory: %s", f.getAbsolutePath()); + Path p = getDir(sitePaths, schema); + boolean isDir = Files.isDirectory(p); + if (Files.exists(p) && !isDir) { + log.warn("Not a directory: %s", p.toAbsolutePath()); } int v = schema.getVersion(); - versions.put(v, new Version(schema, v, exists, getReady(cfg, v))); + versions.put(v, new Version(schema, v, isDir, getReady(cfg, v))); } - for (File f : sitePaths.index_dir.listFiles()) { - if (!f.getName().startsWith(CHANGES_PREFIX)) { - continue; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(sitePaths.index_dir)) { + for (Path p : paths) { + String n = p.getFileName().toString(); + if (!n.startsWith(CHANGES_PREFIX)) { + continue; + } + String versionStr = n.substring(CHANGES_PREFIX.length()); + Integer v = Ints.tryParse(versionStr); + if (v == null || versionStr.length() != 4) { + log.warn("Unrecognized version in index directory: {}", + p.toAbsolutePath()); + continue; + } + if (!versions.containsKey(v)) { + versions.put(v, new Version(null, v, true, getReady(cfg, v))); + } } - String versionStr = f.getName().substring(CHANGES_PREFIX.length()); - Integer v = Ints.tryParse(versionStr); - if (v == null || versionStr.length() != 4) { - log.warn("Unrecognized version in index directory: {}", - f.getAbsolutePath()); - continue; - } - if (!versions.containsKey(v)) { - versions.put(v, new Version(null, v, true, getReady(cfg, v))); - } + } catch (IOException e) { + log.error("Error scanning index directory: " + sitePaths.index_dir, e); } return versions; }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java index 28af057..218bb71 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -148,7 +148,7 @@ } else if (p.getType() == FieldType.FULL_TEXT) { return fullTextQuery(p); } else { - throw badFieldType(p.getType()); + throw FieldType.badFieldType(p.getType()); } } @@ -164,8 +164,8 @@ try { // Can't use IntPredicate because it and IndexPredicate are different // subclasses of OperatorPredicate. - value = Integer.valueOf(p.getValue()); - } catch (IllegalArgumentException e) { + value = Integer.parseInt(p.getValue()); + } catch (NumberFormatException e) { throw new QueryParseException("not an integer: " + p.getValue()); } return new TermQuery(intTerm(p.getField().getName(), value)); @@ -249,8 +249,4 @@ public int toIndexTimeInMinutes(Date ts) { return (int) (ts.getTime() / 60000); } - - public static IllegalArgumentException badFieldType(FieldType<?> t) { - return new IllegalArgumentException("unknown index field type " + t); - } }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java index e024f76..5778008 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
@@ -28,17 +28,17 @@ import org.apache.lucene.index.TrackingIndexWriter; import org.apache.lucene.search.ControlledRealTimeReopenThread; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.ReferenceManager; import org.apache.lucene.search.ReferenceManager.RefreshListener; import org.apache.lucene.search.SearcherFactory; -import org.apache.lucene.search.SearcherManager; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -52,16 +52,19 @@ private final Directory dir; private final TrackingIndexWriter writer; - private final SearcherManager searcherManager; + private final ReferenceManager<IndexSearcher> searcherManager; private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread; private final Set<NrtFuture> notDoneNrtFutures; - SubIndex(File file, GerritIndexWriterConfig writerConfig) throws IOException { - this(FSDirectory.open(file), file.getName(), writerConfig); + SubIndex(Path path, GerritIndexWriterConfig writerConfig, + SearcherFactory searcherFactory) throws IOException { + this(FSDirectory.open(path), path.getFileName().toString(), writerConfig, + searcherFactory); } SubIndex(Directory dir, final String dirName, - GerritIndexWriterConfig writerConfig) throws IOException { + GerritIndexWriterConfig writerConfig, + SearcherFactory searcherFactory) throws IOException { this.dir = dir; IndexWriter delegateWriter; long commitPeriod = writerConfig.getCommitWithinMs(); @@ -103,8 +106,8 @@ }, commitPeriod, commitPeriod, MILLISECONDS); } writer = new TrackingIndexWriter(delegateWriter); - searcherManager = new SearcherManager( - writer.getIndexWriter(), true, new SearcherFactory()); + searcherManager = new WrappableSearcherManager( + writer.getIndexWriter(), true, searcherFactory); notDoneNrtFutures = Sets.newConcurrentHashSet(); @@ -124,6 +127,8 @@ // searching generation being up to date when calling // reopenThread.waitForGeneration(gen, 0), therefore the reopen thread's // internal listener needs to be called first. + // TODO(dborowitz): This may have been fixed by + // http://issues.apache.org/jira/browse/LUCENE-5461 searcherManager.addListener(new RefreshListener() { @Override public void beforeRefresh() throws IOException { @@ -157,12 +162,9 @@ } try { - writer.getIndexWriter().commit(); - try { - writer.getIndexWriter().close(); - } catch (AlreadyClosedException e) { - // Ignore. - } + writer.getIndexWriter().close(); + } catch (AlreadyClosedException e) { + // Ignore. } catch (IOException e) { log.warn("error closing Lucene writer", e); }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java new file mode 100644 index 0000000..981f909 --- /dev/null +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java
@@ -0,0 +1,220 @@ +package com.google.gerrit.lucene; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.FilterDirectoryReader; +import org.apache.lucene.index.FilterLeafReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.ReferenceManager; +import org.apache.lucene.search.SearcherFactory; +import org.apache.lucene.store.Directory; + +import java.io.IOException; + +/** + * Utility class to safely share {@link IndexSearcher} instances across multiple + * threads, while periodically reopening. This class ensures each searcher is + * closed only once all threads have finished using it. + * + * <p> + * Use {@link #acquire} to obtain the current searcher, and {@link #release} to + * release it, like this: + * + * <pre class="prettyprint"> + * IndexSearcher s = manager.acquire(); + * try { + * // Do searching, doc retrieval, etc. with s + * } finally { + * manager.release(s); + * } + * // Do not use s after this! + * s = null; + * </pre> + * + * <p> + * In addition you should periodically call {@link #maybeRefresh}. While it's + * possible to call this just before running each query, this is discouraged + * since it penalizes the unlucky queries that need to refresh. It's better to use + * a separate background thread, that periodically calls {@link #maybeRefresh}. Finally, + * be sure to call {@link #close} once you are done. + * + * @see SearcherFactory + * + * @lucene.experimental + */ +// This file was copied from: +// https://github.com/apache/lucene-solr/blob/lucene_solr_5_0/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java +// The only change (other than class name and import fixes) +// is to skip the check in getSearcher that searcherFactory.newSearcher wraps +// the provided searcher exactly. +final class WrappableSearcherManager extends ReferenceManager<IndexSearcher> { + + private final SearcherFactory searcherFactory; + + /** + * Creates and returns a new SearcherManager from the given + * {@link IndexWriter}. + * + * @param writer + * the IndexWriter to open the IndexReader from. + * @param applyAllDeletes + * If <code>true</code>, all buffered deletes will be applied (made + * visible) in the {@link IndexSearcher} / {@link DirectoryReader}. + * If <code>false</code>, the deletes may or may not be applied, but + * remain buffered (in IndexWriter) so that they will be applied in + * the future. Applying deletes can be costly, so if your app can + * tolerate deleted documents being returned you might gain some + * performance by passing <code>false</code>. See + * {@link DirectoryReader#openIfChanged(DirectoryReader, IndexWriter, boolean)}. + * @param searcherFactory + * An optional {@link SearcherFactory}. Pass <code>null</code> if you + * don't require the searcher to be warmed before going live or other + * custom behavior. + * + * @throws IOException if there is a low-level I/O error + */ + public WrappableSearcherManager(IndexWriter writer, boolean applyAllDeletes, SearcherFactory searcherFactory) throws IOException { + if (searcherFactory == null) { + searcherFactory = new SearcherFactory(); + } + this.searcherFactory = searcherFactory; + current = getSearcher(searcherFactory, DirectoryReader.open(writer, applyAllDeletes)); + } + + /** + * Creates and returns a new SearcherManager from the given {@link Directory}. + * @param dir the directory to open the DirectoryReader on. + * @param searcherFactory An optional {@link SearcherFactory}. Pass + * <code>null</code> if you don't require the searcher to be warmed + * before going live or other custom behavior. + * + * @throws IOException if there is a low-level I/O error + */ + public WrappableSearcherManager(Directory dir, SearcherFactory searcherFactory) throws IOException { + if (searcherFactory == null) { + searcherFactory = new SearcherFactory(); + } + this.searcherFactory = searcherFactory; + current = getSearcher(searcherFactory, DirectoryReader.open(dir)); + } + + /** + * Creates and returns a new SearcherManager from an existing {@link DirectoryReader}. Note that + * this steals the incoming reference. + * + * @param reader the DirectoryReader. + * @param searcherFactory An optional {@link SearcherFactory}. Pass + * <code>null</code> if you don't require the searcher to be warmed + * before going live or other custom behavior. + * + * @throws IOException if there is a low-level I/O error + */ + public WrappableSearcherManager(DirectoryReader reader, SearcherFactory searcherFactory) throws IOException { + if (searcherFactory == null) { + searcherFactory = new SearcherFactory(); + } + this.searcherFactory = searcherFactory; + this.current = getSearcher(searcherFactory, reader); + } + + @Override + protected void decRef(IndexSearcher reference) throws IOException { + reference.getIndexReader().decRef(); + } + + @Override + protected IndexSearcher refreshIfNeeded(IndexSearcher referenceToRefresh) throws IOException { + final IndexReader r = referenceToRefresh.getIndexReader(); + assert r instanceof DirectoryReader: "searcher's IndexReader should be a DirectoryReader, but got " + r; + final IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) r); + if (newReader == null) { + return null; + } else { + return getSearcher(searcherFactory, newReader); + } + } + + @Override + protected boolean tryIncRef(IndexSearcher reference) { + return reference.getIndexReader().tryIncRef(); + } + + @Override + protected int getRefCount(IndexSearcher reference) { + return reference.getIndexReader().getRefCount(); + } + + /** + * Returns <code>true</code> if no changes have occured since this searcher + * ie. reader was opened, otherwise <code>false</code>. + * @see DirectoryReader#isCurrent() + */ + public boolean isSearcherCurrent() throws IOException { + final IndexSearcher searcher = acquire(); + try { + final IndexReader r = searcher.getIndexReader(); + assert r instanceof DirectoryReader: "searcher's IndexReader should be a DirectoryReader, but got " + r; + return ((DirectoryReader) r).isCurrent(); + } finally { + release(searcher); + } + } + + /** Expert: creates a searcher from the provided {@link + * IndexReader} using the provided {@link + * SearcherFactory}. NOTE: this decRefs incoming reader + * on throwing an exception. */ + @SuppressWarnings("resource") + public static IndexSearcher getSearcher(SearcherFactory searcherFactory, IndexReader reader) throws IOException { + boolean success = false; + final IndexSearcher searcher; + try { + searcher = searcherFactory.newSearcher(reader); + // Modification for Gerrit: Allow searcherFactory to transitively wrap the + // provided reader. + IndexReader unwrapped = searcher.getIndexReader(); + while (true) { + if (unwrapped == reader) { + break; + } else if (unwrapped instanceof FilterDirectoryReader) { + unwrapped = ((FilterDirectoryReader) unwrapped).getDelegate(); + } else if (unwrapped instanceof FilterLeafReader) { + unwrapped = ((FilterLeafReader) unwrapped).getDelegate(); + } else { + break; + } + } + + if (unwrapped != reader) { + throw new IllegalStateException( + "SearcherFactory must wrap the provided reader (got " + + searcher.getIndexReader() + + " but expected " + reader + ")"); + } + success = true; + } finally { + if (!success) { + reader.decRef(); + } + } + return searcher; + } +}
diff --git a/gerrit-main/src/main/java/Main.java b/gerrit-main/src/main/java/Main.java index d3c9fdd..54cf20e 100644 --- a/gerrit-main/src/main/java/Main.java +++ b/gerrit-main/src/main/java/Main.java
@@ -20,7 +20,7 @@ // to jump into the real main code. // - public static void main(final String argv[]) throws Exception { + public static void main(final String[] argv) throws Exception { if (onSupportedJavaVersion()) { com.google.gerrit.launcher.GerritLauncher.main(argv);
diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK index 9a317cd..10d6dda 100644 --- a/gerrit-pgm/BUCK +++ b/gerrit-pgm/BUCK
@@ -18,7 +18,8 @@ '//lib/guice:guice-servlet', '//lib/jgit:jgit', '//lib/log:api', - '//lib/log:log4j', + '//lib/log:jsonevent-layout', + '//lib/log:log4j' ] java_library( @@ -91,7 +92,7 @@ java_library( name = 'pgm', - srcs = glob([SRCS + '*.java']), + srcs = glob([SRCS + '*.java', SRCS + 'rules/*.java']), resources = glob([RSRCS + '*']), deps = DEPS + [ ':http', @@ -106,7 +107,9 @@ '//lib:args4j', '//lib:gwtorm', '//lib:servlet-api-3_1', - '//lib/prolog:prolog-cafe', + '//lib/prolog:cafeteria', + '//lib/prolog:compiler', + '//lib/prolog:runtime', ], provided_deps = ['//gerrit-launcher:launcher'], visibility = [ @@ -125,7 +128,9 @@ ':init', ':init-api', ':pgm', + '//gerrit-common:server', '//gerrit-server:server', + '//lib:guava', '//lib:junit', '//lib/easymock:easymock', '//lib/guice:guice',
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java index 2b9af2f..1afa243 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -15,6 +15,7 @@ package com.google.gerrit.pgm; import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; @@ -93,10 +94,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -148,9 +149,10 @@ private Injector sshInjector; private Injector webInjector; private Injector httpdInjector; - private File runFile; + private Path runFile; private boolean test; private AbstractModule luceneModule; + private Module emailModule; private Runnable serverStarted; @@ -184,7 +186,7 @@ }); if (runId != null) { - runFile = new File(new File(getSitePath(), "logs"), "gerrit.run"); + runFile = getSitePath().resolve("logs").resolve("gerrit.run"); } if (httpd == null) { @@ -195,11 +197,6 @@ throw die("No services enabled, nothing to do"); } - if (consoleLog) { - } else { - manager.add(ErrorLogFile.start(getSitePath())); - } - try { start(); RuntimeShutdown.add(new Runnable() { @@ -207,7 +204,11 @@ public void run() { log.info("caught shutdown, cleaning up"); if (runId != null) { - runFile.delete(); + try { + Files.delete(runFile); + } catch (IOException err) { + log.warn("failed to delete " + runFile, err); + } } manager.stop(); } @@ -216,15 +217,8 @@ log.info("Gerrit Code Review " + myVersion() + " ready"); if (runId != null) { try { - runFile.createNewFile(); - runFile.setReadable(true, false); - - FileOutputStream out = new FileOutputStream(runFile); - try { - out.write((runId + "\n").getBytes("UTF-8")); - } finally { - out.close(); - } + Files.write(runFile, (runId + "\n").getBytes(UTF_8)); + runFile.toFile().setReadable(true, false); } catch (IOException err) { log.warn("Cannot write --run-id to " + runFile, err); } @@ -264,13 +258,18 @@ } @VisibleForTesting + public void setEmailModuleForTesting(Module module) { + emailModule = module; + } + + @VisibleForTesting public void setLuceneModule(LuceneIndexModule m) { luceneModule = m; test = true; } @VisibleForTesting - public void start() { + public void start() throws IOException { if (dbInjector == null) { dbInjector = createDbInjector(MULTI_USER); } @@ -280,6 +279,11 @@ .setDbCfgInjector(dbInjector, cfgInjector); manager.add(dbInjector, cfgInjector, sysInjector); + if (!consoleLog) { + manager.add(ErrorLogFile.start(getSitePath(), + cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)))); + } + sshd &= !sshdOff(); if (sshd) { initSshd(); @@ -325,7 +329,11 @@ modules.add(new ChangeCacheImplModule(slave)); modules.add(new InternalAccountDirectory.Module()); modules.add(new DefaultCacheFactory.Module()); - modules.add(new SmtpEmailSender.Module()); + if (emailModule != null) { + modules.add(emailModule); + } else { + modules.add(new SmtpEmailSender.Module()); + } modules.add(new SignedTokenEmailTokenVerifier.Module()); modules.add(new PluginRestApiModule()); modules.add(new RestCacheAdminModule());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java index 38b1824..e2775c1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -37,8 +37,8 @@ import org.kohsuke.args4j.Option; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -70,7 +70,7 @@ super(new WarDistribution(), null); } - public Init(File sitePath) { + public Init(Path sitePath) { super(sitePath, true, true, new WarDistribution(), null); batchMode = true; noAutoStart = true; @@ -106,7 +106,7 @@ modules.add(new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(getSitePath()); + bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath()); bind(Browser.class); bind(String.class).annotatedWith(SecureStoreClassName.class) .toProvider(Providers.of(getConfiguredSecureStoreClass())); @@ -157,8 +157,8 @@ } void startDaemon(SiteRun run) { - final String[] argv = {run.site.gerrit_sh.getAbsolutePath(), "start"}; - final Process proc; + String[] argv = {run.site.gerrit_sh.toAbsolutePath().toString(), "start"}; + Process proc; try { System.err.println("Executing " + argv[0] + " " + argv[1]); proc = Runtime.getRuntime().exec(argv); @@ -177,7 +177,7 @@ for (;;) { try { - final int rc = proc.waitFor(); + int rc = proc.waitFor(); if (rc != 0) { System.err.println("error: cannot start Gerrit: exit status " + rc); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Ls.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Ls.java index 7e7b602..2bcd6ea 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Ls.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Ls.java
@@ -39,6 +39,7 @@ show &= !ze.isDirectory(); show &= !name.startsWith("WEB-INF/classes/"); show &= !name.startsWith("WEB-INF/lib/"); + show &= !name.startsWith("WEB-INF/pgm-lib/"); show &= !name.equals("WEB-INF/web.xml"); if (show) { if (name.startsWith("WEB-INF/")) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java index fa434a6..58162a1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/PrologShell.java
@@ -16,11 +16,10 @@ import com.google.gerrit.pgm.util.AbstractProgram; +import com.googlecode.prolog_cafe.exceptions.HaltException; import com.googlecode.prolog_cafe.lang.BufferingPrologControl; -import com.googlecode.prolog_cafe.lang.HaltException; import com.googlecode.prolog_cafe.lang.Prolog; import com.googlecode.prolog_cafe.lang.PrologClassLoader; -import com.googlecode.prolog_cafe.lang.PrologMain; import com.googlecode.prolog_cafe.lang.SymbolTerm; import org.kohsuke.args4j.Option; @@ -28,7 +27,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; public class PrologShell extends AbstractProgram { @@ -41,14 +39,10 @@ BufferingPrologControl pcl = new BufferingPrologControl(); pcl.setPrologClassLoader(new PrologClassLoader(getClass().getClassLoader())); - pcl.setEnabled(EnumSet.allOf(Prolog.Feature.class), false); pcl.setEnabled(Prolog.Feature.IO, true); - pcl.setEnabled(Prolog.Feature.STATISTICS_RUNTIME, true); - + pcl.setEnabled(Prolog.Feature.STATISTICS, true); + pcl.configureUserIO(System.in, System.out, System.err); pcl.initialize(Prolog.BUILTIN); - pcl.execute(Prolog.BUILTIN, "set_prolog_flag", - SymbolTerm.intern("print_stack_trace"), - SymbolTerm.intern("on")); for (String file : fileName) { String path; @@ -76,8 +70,6 @@ System.err.format("Gerrit Code Review %s - Interactive Prolog Shell", com.google.gerrit.common.Version.getVersion()); System.err.println(); - System.err.println("based on " + PrologMain.VERSION); - System.err.println(" " + PrologMain.COPYRIGHT); System.err.println("(type Ctrl-D or \"halt.\" to exit," + " \"['path/to/file.pl'].\" to load a file)"); System.err.println();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java index ab20f53..a29a8e0 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNotedb.java
@@ -244,7 +244,7 @@ } } - private class RebuildListener implements Runnable { + private static class RebuildListener implements Runnable { private Change.Id changeId; private ListenableFuture<?> future; private AtomicBoolean ok;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java index 17a54d4..8f1b354 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
@@ -17,15 +17,15 @@ import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER; import com.google.gerrit.lifecycle.LifecycleManager; +import com.google.gerrit.pgm.rules.PrologCompiler; import com.google.gerrit.pgm.util.SiteProgram; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.rules.PrologCompiler; import com.google.gerrit.server.config.FactoryModule; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.inject.Inject; import com.google.inject.Injector; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; import org.eclipse.jgit.lib.Repository; import org.kohsuke.args4j.Argument;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java index f2feae1..ac84e82 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java
@@ -19,7 +19,6 @@ import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Iterables; -import com.google.common.io.Files; import com.google.gerrit.common.IoUtil; import com.google.gerrit.common.SiteLibraryLoaderUtil; import com.google.gerrit.pgm.util.SiteProgram; @@ -37,8 +36,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.jar.JarFile; @@ -47,7 +48,7 @@ public class SwitchSecureStore extends SiteProgram { private static String getSecureStoreClassFromGerritConfig(SitePaths sitePaths) { FileBasedConfig cfg = - new FileBasedConfig(sitePaths.gerrit_config, FS.DETECTED); + new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED); try { cfg.load(); } catch (IOException | ConfigInvalidException e) { @@ -67,14 +68,14 @@ @Override public int run() throws Exception { SitePaths sitePaths = new SitePaths(getSitePath()); - File newSecureStoreFile = new File(newSecureStoreLib); - if (!newSecureStoreFile.exists()) { - log.error(String.format("File %s doesn't exists", - newSecureStoreFile.getAbsolutePath())); + Path newSecureStorePath = Paths.get(newSecureStoreLib); + if (!Files.exists(newSecureStorePath)) { + log.error(String.format("File %s doesn't exist", + newSecureStorePath.toAbsolutePath())); return -1; } - String newSecureStore = getNewSecureStoreClassName(newSecureStoreFile); + String newSecureStore = getNewSecureStoreClassName(newSecureStorePath); String currentSecureStoreName = getCurrentSecureStoreClassName(sitePaths); if (currentSecureStoreName.equals(newSecureStore)) { @@ -83,7 +84,7 @@ return -1; } - IoUtil.loadJARs(newSecureStoreFile); + IoUtil.loadJARs(newSecureStorePath); SiteLibraryLoaderUtil.loadSiteLib(sitePaths.lib_dir); log.info("Current secureStoreClass property ({}) will be replaced with {}", @@ -96,7 +97,7 @@ migrateProperties(currentStore, newStore); removeOldLib(sitePaths, currentSecureStoreName); - copyNewLib(sitePaths, newSecureStoreFile); + copyNewLib(sitePaths, newSecureStorePath); updateGerritConfig(sitePaths, newSecureStore); @@ -123,14 +124,17 @@ } } - private void removeOldLib(SitePaths sitePaths, String currentSecureStoreName) { - File oldSecureStore = + private void removeOldLib(SitePaths sitePaths, String currentSecureStoreName) + throws IOException { + Path oldSecureStore = findJarWithSecureStore(sitePaths, currentSecureStoreName); if (oldSecureStore != null) { log.info("Removing old SecureStore ({}) from lib/ directory", - oldSecureStore.getName()); - if (!oldSecureStore.delete()) { - log.error("Cannot remove {}", oldSecureStore.getAbsolutePath()); + oldSecureStore.getFileName()); + try { + Files.delete(oldSecureStore); + } catch (IOException e) { + log.error("Cannot remove {}", oldSecureStore.toAbsolutePath(), e); } } else { log.info("Cannot find jar with old SecureStore ({}) in lib/ directory", @@ -138,12 +142,12 @@ } } - private void copyNewLib(SitePaths sitePaths, File newSecureStoreFile) + private void copyNewLib(SitePaths sitePaths, Path newSecureStorePath) throws IOException { log.info("Copy new SecureStore ({}) into lib/ directory", - newSecureStoreFile.getName()); - Files.copy(newSecureStoreFile, new File(sitePaths.lib_dir, - newSecureStoreFile.getName())); + newSecureStorePath.getFileName()); + Files.copy(newSecureStorePath, + sitePaths.lib_dir.resolve(newSecureStorePath.getFileName())); } private void updateGerritConfig(SitePaths sitePaths, String newSecureStore) @@ -151,13 +155,13 @@ log.info("Set gerrit.secureStoreClass property of gerrit.config to {}", newSecureStore); FileBasedConfig config = - new FileBasedConfig(sitePaths.gerrit_config, FS.DETECTED); + new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED); config.load(); config.setString("gerrit", null, "secureStoreClass", newSecureStore); config.save(); } - private String getNewSecureStoreClassName(File secureStore) + private String getNewSecureStoreClassName(Path secureStore) throws IOException { JarScanner scanner = new JarScanner(secureStore); List<String> newSecureStores = @@ -165,12 +169,12 @@ if (newSecureStores.isEmpty()) { throw new RuntimeException(String.format( "Cannot find implementation of SecureStore interface in %s", - secureStore.getAbsolutePath())); + secureStore.toAbsolutePath())); } if (newSecureStores.size() > 1) { throw new RuntimeException(String.format( "Found too many implementations of SecureStore:\n%s\nin %s", Joiner - .on("\n").join(newSecureStores), secureStore.getAbsolutePath())); + .on("\n").join(newSecureStores), secureStore.toAbsolutePath())); } return Iterables.getOnlyElement(newSecureStores); } @@ -195,15 +199,12 @@ } } - private File findJarWithSecureStore(SitePaths sitePaths, - String secureStoreClass) { - File[] jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir); - if (jars == null || jars.length == 0) { - return null; - } + private Path findJarWithSecureStore(SitePaths sitePaths, + String secureStoreClass) throws IOException { + List<Path> jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir); String secureStoreClassPath = secureStoreClass.replace('.', '/') + ".class"; - for (File jar : jars) { - try (JarFile jarFile = new JarFile(jar)) { + for (Path jar : jars) { + try (JarFile jarFile = new JarFile(jar.toFile())) { ZipEntry entry = jarFile.getEntry(secureStoreClassPath); if (entry != null) { return jar;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 907624d..80d69f1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -81,6 +81,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; import java.util.Enumeration; @@ -220,22 +222,22 @@ } else if ("https".equals(u.getScheme())) { SslContextFactory ssl = new SslContextFactory(); - final File keystore = getFile(cfg, "sslkeystore", "etc/keystore"); + final Path keystore = getFile(cfg, "sslkeystore", "etc/keystore"); String password = cfg.getString("httpd", null, "sslkeypassword"); if (password == null) { password = "gerrit"; } - ssl.setKeyStorePath(keystore.getAbsolutePath()); - ssl.setTrustStorePath(keystore.getAbsolutePath()); + ssl.setKeyStorePath(keystore.toAbsolutePath().toString()); + ssl.setTrustStorePath(keystore.toAbsolutePath().toString()); ssl.setKeyStorePassword(password); ssl.setTrustStorePassword(password); if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType)) { ssl.setNeedClientAuth(true); - File crl = getFile(cfg, "sslcrl", "etc/crl.pem"); - if (crl.exists()) { - ssl.setCrlPath(crl.getAbsolutePath()); + Path crl = getFile(cfg, "sslcrl", "etc/crl.pem"); + if (Files.exists(crl)) { + ssl.setCrlPath(crl.toAbsolutePath().toString()); ssl.setValidatePeerCerts(true); } } @@ -340,7 +342,7 @@ return r; } - private File getFile(final Config cfg, final String name, final String def) { + private Path getFile(Config cfg, String name, String def) { String path = cfg.getString("httpd", null, name); if (path == null || path.length() == 0) { path = def; @@ -525,11 +527,13 @@ final ZipEntry ze = e.nextElement(); final String name = ze.getName(); - if (ze.isDirectory()) continue; - if (name.startsWith("WEB-INF/")) continue; - if (name.startsWith("META-INF/")) continue; - if (name.startsWith("com/google/gerrit/launcher/")) continue; - if (name.equals("Main.class")) continue; + if (ze.isDirectory() + || name.startsWith("WEB-INF/") + || name.startsWith("META-INF/") + || name.startsWith("com/google/gerrit/launcher/") + || name.equals("Main.class")) { + continue; + } final File rawtmp = new File(dstwar, name); mkdir(rawtmp.getParentFile()); @@ -559,8 +563,9 @@ private static void mkdir(File dir) throws IOException { if (!dir.isDirectory()) { mkdir(dir.getParentFile()); - if (!dir.mkdir()) + if (!dir.mkdir()) { throw new IOException("Cannot mkdir " + dir.getAbsolutePath()); + } dir.deleteOnExit(); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java index c9e76c8..534ef050 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -56,9 +56,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -86,12 +91,12 @@ this.pluginsToInstall = pluginsToInstall; } - public BaseInit(File sitePath, boolean standalone, boolean initDb, + public BaseInit(Path sitePath, boolean standalone, boolean initDb, PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) { this(sitePath, null, standalone, initDb, pluginsDistribution, pluginsToInstall); } - public BaseInit(File sitePath, final Provider<DataSource> dsProvider, + public BaseInit(Path sitePath, final Provider<DataSource> dsProvider, boolean standalone, boolean initDb, PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) { super(sitePath, dsProvider); @@ -132,7 +137,7 @@ throw failure; } - System.err.println("Initialized " + getSitePath().getCanonicalPath()); + System.err.println("Initialized " + getSitePath().toRealPath().normalize()); afterInit(run); return 0; } @@ -208,7 +213,7 @@ private SiteInit createSiteInit() { final ConsoleUI ui = getConsoleUI(); - final File sitePath = getSitePath(); + final Path sitePath = getSitePath(); final List<Module> m = new ArrayList<>(); final SecureStoreInitData secureStoreInitData = discoverSecureStoreClass(); final String currentSecureStoreClassName = getConfiguredSecureStoreClass(); @@ -228,7 +233,7 @@ @Override protected void configure() { bind(ConsoleUI.class).toInstance(ui); - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath); List<String> plugins = MoreObjects.firstNonNull( getInstallPlugins(), Lists.<String> newArrayList()); @@ -287,8 +292,8 @@ } try { - File secureStoreLib = new File(secureStore); - if (!secureStoreLib.exists()) { + Path secureStoreLib = Paths.get(secureStore); + if (!Files.exists(secureStoreLib)) { throw new InvalidSecureStoreException(String.format( "File %s doesn't exist", secureStore)); } @@ -408,15 +413,41 @@ return sysInjector; } - private static void recursiveDelete(File path) { - File[] entries = path.listFiles(); - if (entries != null) { - for (File e : entries) { - recursiveDelete(e); - } - } - if (!path.delete() && path.exists()) { - System.err.println("warn: Cannot remove " + path); + private static void recursiveDelete(Path path) { + final String msg = "warn: Cannot remove "; + try { + Files.walkFileTree(path, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) + throws IOException { + try { + Files.delete(f); + } catch (IOException e) { + System.err.println(msg + f); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException err) { + try { + // Previously warned if err was not null; if dir is not empty as a + // result, will cause an error that will be logged below. + Files.delete(dir); + } catch (IOException e) { + System.err.println(msg + dir); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path f, IOException e) { + System.err.println(msg + f); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + System.err.println(msg + path); } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java index 5a1eab2..98bee3d 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java
@@ -31,7 +31,7 @@ private final Config cfg; @Inject - Browser(final @GerritServerConfig Config cfg) { + Browser(@GerritServerConfig final Config cfg) { this.cfg = cfg; }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java index e20346a..6d60ad1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java
@@ -14,12 +14,14 @@ package com.google.gerrit.pgm.init; -import com.google.gerrit.pgm.init.api.InitUtil; +import static com.google.gerrit.pgm.init.api.InitUtil.die; + +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.Section; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; -import java.io.File; +import java.nio.file.Path; class H2Initializer implements DatabaseConfigInitializer { @@ -33,18 +35,17 @@ @Override public void initConfig(Section databaseSection) { String path = databaseSection.get("database"); + Path db; if (path == null) { - path = "db/ReviewDB"; - databaseSection.set("database", path); + db = site.resolve("db").resolve("ReviewDB"); + databaseSection.set("database", db.toString()); + } else { + db = site.resolve(path); } - File db = site.resolve(path); if (db == null) { - throw InitUtil.die("database.database must be supplied for H2"); + throw die("database.database must be supplied for H2"); } - db = db.getParentFile(); - if (!db.exists() && !db.mkdirs()) { - throw InitUtil.die("cannot create database.database " - + db.getAbsolutePath()); - } + db = db.getParent(); + FileUtil.mkdirsOrDie(db, "cannot create database.database"); } -} \ No newline at end of file +}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java index c1f0090..e0cb4c4 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -23,6 +23,7 @@ import com.google.gerrit.reviewdb.client.AccountExternalId; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroupMember; +import com.google.gerrit.reviewdb.client.AccountSshKey; import com.google.gerrit.reviewdb.client.AuthType; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gwtorm.server.SchemaFactory; @@ -30,6 +31,11 @@ import org.apache.commons.validator.routines.EmailValidator; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; public class InitAdminUser implements InitStep { @@ -70,8 +76,9 @@ Account.Id id = new Account.Id(db.nextAccountId()); String username = ui.readString("admin", "username"); String name = ui.readString("Administrator", "name"); - String email = readEmail(); String httpPassword = ui.readString("secret", "HTTP password"); + AccountSshKey sshKey = readSshKey(id); + String email = readEmail(sshKey); AccountExternalId extUser = new AccountExternalId(id, new AccountExternalId.Key( @@ -98,6 +105,10 @@ new AccountGroupMember(new AccountGroupMember.Key(id, new AccountGroup.Id(1))); db.accountGroupMembers().insert(Collections.singleton(m)); + + if (sshKey != null) { + db.accountSshKeys().insert(Collections.singleton(sshKey)); + } } } } finally { @@ -105,12 +116,47 @@ } } - private String readEmail() { - String email = ui.readString("admin@example.com", "email"); + private String readEmail(AccountSshKey sshKey) { + String defaultEmail = "admin@example.com"; + if (sshKey != null && sshKey.getComment() != null) { + String c = sshKey.getComment().trim(); + if (EmailValidator.getInstance().isValid(c)) { + defaultEmail = c; + } + } + return readEmail(defaultEmail); + } + + private String readEmail(String defaultEmail) { + String email = ui.readString(defaultEmail, "email"); if (email != null && !EmailValidator.getInstance().isValid(email)) { ui.message("error: invalid email address\n"); - return readEmail(); + return readEmail(defaultEmail); } return email; } + + private AccountSshKey readSshKey(Account.Id id) throws IOException { + String defaultPublicSshKeyFile = ""; + Path defaultPublicSshKeyPath = + Paths.get(System.getProperty("user.home"), ".ssh", "id_rsa.pub"); + if (Files.exists(defaultPublicSshKeyPath)) { + defaultPublicSshKeyFile = defaultPublicSshKeyPath.toString(); + } + String publicSshKeyFile = + ui.readString(defaultPublicSshKeyFile, "public SSH key file"); + return !Strings.isNullOrEmpty(publicSshKeyFile) + ? createSshKey(id, publicSshKeyFile) : null; + } + + private AccountSshKey createSshKey(Account.Id id, String keyFile) + throws IOException { + Path p = Paths.get(keyFile); + if (!Files.exists(p)) { + throw new IOException(String.format( + "Cannot add public SSH key: %s is not a file", keyFile)); + } + String content = new String(Files.readAllBytes(p), StandardCharsets.UTF_8); + return new AccountSshKey(new AccountSshKey.Id(id, 0), content); + } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java index 8da4a03..4e5d044 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java
@@ -14,15 +14,14 @@ package com.google.gerrit.pgm.init; -import static com.google.gerrit.pgm.init.api.InitUtil.die; - +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.pgm.init.api.Section; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; +import java.nio.file.Path; /** Initialize the {@code cache} configuration section. */ @Singleton @@ -52,10 +51,8 @@ cache.set("directory", path); } - final File loc = site.resolve(path); - if (!loc.exists() && !loc.mkdirs()) { - throw die("cannot create cache.directory " + loc.getAbsolutePath()); - } + Path loc = site.resolve(path); + FileUtil.mkdirsOrDie(loc, "cannot create cache.directory"); } @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java index f830854..60ff665 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java
@@ -17,6 +17,7 @@ import static com.google.gerrit.pgm.init.api.InitUtil.die; import static com.google.gerrit.pgm.init.api.InitUtil.username; +import com.google.common.io.ByteStreams; import com.google.gerrit.launcher.GerritLauncher; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -28,11 +29,12 @@ import org.eclipse.jgit.internal.storage.file.LockFile; import org.eclipse.jgit.util.FS; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code container} configuration section. */ @Singleton @@ -56,9 +58,9 @@ container.string("Run as", "user", username()); container.string("Java runtime", "javaHome", javaHome()); - File myWar; + Path myWar; try { - myWar = GerritLauncher.getDistributionArchive(); + myWar = GerritLauncher.getDistributionArchive().toPath(); } catch (FileNotFoundException e) { System.err.println("warn: Cannot find distribution archive (e.g. gerrit.war)"); myWar = null; @@ -66,53 +68,41 @@ String path = container.get("war"); if (path != null) { - path = container.string("Gerrit runtime", "war", // - myWar != null ? myWar.getAbsolutePath() : null); + path = container.string("Gerrit runtime", "war", + myWar != null ? myWar.toAbsolutePath().toString() : null); if (path == null || path.isEmpty()) { throw die("container.war is required"); } } else if (myWar != null) { final boolean copy; - final File siteWar = site.gerrit_war; - if (siteWar.exists()) { - copy = ui.yesno(true, "Upgrade %s", siteWar.getPath()); + final Path siteWar = site.gerrit_war; + if (Files.exists(siteWar)) { + copy = ui.yesno(true, "Upgrade %s", siteWar); } else { - copy = ui.yesno(true, "Copy %s to %s", myWar.getName(), siteWar.getPath()); + copy = ui.yesno(true, "Copy %s to %s", myWar.getFileName(), siteWar); if (copy) { container.unset("war"); } else { - container.set("war", myWar.getAbsolutePath()); + container.set("war", myWar.toAbsolutePath().toString()); } } if (copy) { if (!ui.isBatch()) { - System.err.format("Copying %s to %s", myWar.getName(), siteWar.getPath()); + System.err.format("Copying %s to %s", myWar.getFileName(), siteWar); System.err.println(); } - FileInputStream in = new FileInputStream(myWar); - try { - siteWar.getParentFile().mkdirs(); + try (InputStream in = Files.newInputStream(myWar)) { + Files.createDirectories(siteWar.getParent()); - LockFile lf = new LockFile(siteWar, FS.DETECTED); + LockFile lf = new LockFile(siteWar.toFile(), FS.DETECTED); if (!lf.lock()) { throw new IOException("Cannot lock " + siteWar); } - try { - final OutputStream out = lf.getOutputStream(); - try { - final byte[] tmp = new byte[4096]; - for (;;) { - int n = in.read(tmp); - if (n < 0) { - break; - } - out.write(tmp, 0, n); - } - } finally { - out.close(); + try (OutputStream out = lf.getOutputStream()) { + ByteStreams.copy(in, out); } if (!lf.commit()) { throw new IOException("Cannot commit " + siteWar); @@ -120,8 +110,6 @@ } finally { lf.unlock(); } - } finally { - in.close(); } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java index 067b103..d8fd509 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
@@ -16,13 +16,14 @@ import static com.google.gerrit.pgm.init.api.InitUtil.die; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.pgm.init.api.Section; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; +import java.nio.file.Path; /** Initialize the GitRepositoryManager configuration section. */ @Singleton @@ -40,13 +41,11 @@ public void run() { ui.header("Git Repositories"); - File d = gerrit.path("Location of Git repositories", "basePath", "git"); + Path d = gerrit.path("Location of Git repositories", "basePath", "git"); if (d == null) { throw die("gerrit.basePath is required"); } - if (!d.exists() && !d.mkdirs()) { - throw die("Cannot create " + d); - } + FileUtil.mkdirsOrDie(d, "Cannot create"); } @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java index c8f1cd7..a907d46 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java
@@ -29,10 +29,11 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code httpd} configuration section. */ @Singleton @@ -57,7 +58,8 @@ public void run() throws IOException, InterruptedException { ui.header("HTTP Daemon"); - boolean proxy = false, ssl = false; + boolean proxy = false; + boolean ssl = false; String address = "*"; int port = -1; String context = "/"; @@ -149,8 +151,9 @@ return; } - final File store = site.ssl_keystore; - if (!ui.yesno(!store.exists(), "Create new self-signed SSL certificate")) { + Path store = site.ssl_keystore; + if (!ui.yesno(!Files.exists(store), + "Create new self-signed SSL certificate")) { return; } @@ -167,15 +170,17 @@ final String dname = "CN=" + hostname + ",OU=Gerrit Code Review,O=" + domainOf(hostname); - final File tmpdir = new File(site.etc_dir, "tmp.sslcertgen"); - if (!tmpdir.mkdir()) { - throw die("Cannot create directory " + tmpdir); + Path tmpdir = site.etc_dir.resolve("tmp.sslcertgen"); + try { + Files.createDirectory(tmpdir); + } catch (IOException e) { + throw die("Cannot create directory " + tmpdir, e); } chmod(0600, tmpdir); - final File tmpstore = new File(tmpdir, "keystore"); + Path tmpstore = tmpdir.resolve("keystore"); Runtime.getRuntime().exec(new String[] {"keytool", // - "-keystore", tmpstore.getAbsolutePath(), // + "-keystore", tmpstore.toAbsolutePath().toString(), // "-storepass", ssl_pass, // "-genkeypair", // "-alias", hostname, // @@ -186,11 +191,15 @@ }).waitFor(); chmod(0600, tmpstore); - if (!tmpstore.renameTo(store)) { - throw die("Cannot rename " + tmpstore + " to " + store); + try { + Files.move(tmpstore, store); + } catch (IOException e) { + throw die("Cannot rename " + tmpstore + " to " + store, e); } - if (!tmpdir.delete()) { - throw die("Cannot delete " + tmpdir); + try { + Files.delete(tmpdir); + } catch (IOException e) { + throw die("Cannot delete " + tmpdir, e); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java index 8fb05ca..b6b4cc1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java
@@ -26,6 +26,8 @@ @Singleton public class InitLabels implements InitStep { + private static final String KEY_COPY_ALL_SCORES_IF_NO_CODE_CHANGE = + "copyAllScoresIfNoCodeChange"; private static final String KEY_LABEL = "label"; private static final String KEY_FUNCTION = "function"; private static final String KEY_VALUE = "value"; @@ -58,6 +60,7 @@ cfg.setString(KEY_LABEL, LABEL_VERIFIED, KEY_FUNCTION, "MaxWithBlock"); cfg.setStringList(KEY_LABEL, LABEL_VERIFIED, KEY_VALUE, Arrays.asList(new String[] {"-1 Fails", " 0 No score", "+1 Verified"})); + cfg.setBoolean(KEY_LABEL, LABEL_VERIFIED, KEY_COPY_ALL_SCORES_IF_NO_CODE_CHANGE, true); allProjectsConfig.save("Configure 'Verified' label"); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java index fa9f710..3476de5 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
@@ -15,6 +15,7 @@ package com.google.gerrit.pgm.init; import com.google.common.base.MoreObjects; +import com.google.common.collect.Ordering; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -26,23 +27,22 @@ import com.google.inject.Injector; import com.google.inject.Singleton; -import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; @Singleton public class InitPluginStepsLoader { - private final File pluginsDir; + private final Path pluginsDir; private final Injector initInjector; final ConsoleUI ui; @@ -55,10 +55,10 @@ } public Collection<InitStep> getInitSteps() { - List<File> jars = scanJarsInPluginsDirectory(); + List<Path> jars = scanJarsInPluginsDirectory(); ArrayList<InitStep> pluginsInitSteps = new ArrayList<>(); - for (File jar : jars) { + for (Path jar : jars) { InitStep init = loadInitStep(jar); if (init != null) { pluginsInitSteps.add(init); @@ -68,12 +68,12 @@ } @SuppressWarnings("resource") - private InitStep loadInitStep(File jar) { + private InitStep loadInitStep(Path jar) { try { URLClassLoader pluginLoader = - new URLClassLoader(new URL[] {jar.toURI().toURL()}, + new URLClassLoader(new URL[] {jar.toUri().toURL()}, InitPluginStepsLoader.class.getClassLoader()); - try (JarFile jarFile = new JarFile(jar)) { + try (JarFile jarFile = new JarFile(jar.toFile())) { Attributes jarFileAttributes = jarFile.getManifest().getMainAttributes(); String initClassName = jarFileAttributes.getValue("Gerrit-InitStep"); if (initClassName == null) { @@ -86,12 +86,12 @@ } catch (ClassCastException e) { ui.message( "WARN: InitStep from plugin %s does not implement %s (Exception: %s)\n", - jar.getName(), InitStep.class.getName(), e.getMessage()); + jar.getFileName(), InitStep.class.getName(), e.getMessage()); return null; } catch (NoClassDefFoundError e) { ui.message( "WARN: Failed to run InitStep from plugin %s (Missing class: %s)\n", - jar.getName(), e.getMessage()); + jar.getFileName(), e.getMessage()); return null; } } catch (Exception e) { @@ -102,11 +102,10 @@ } } - private Injector getPluginInjector(final File jarFile) throws IOException { - final String pluginName = - MoreObjects.firstNonNull( - JarPluginProvider.getJarPluginName(jarFile), - PluginLoader.nameOf(jarFile)); + private Injector getPluginInjector(Path jarPath) throws IOException { + final String pluginName = MoreObjects.firstNonNull( + JarPluginProvider.getJarPluginName(jarPath), + PluginLoader.nameOf(jarPath)); return initInjector.createChildInjector(new AbstractModule() { @Override protected void configure() { @@ -116,27 +115,24 @@ }); } - private List<File> scanJarsInPluginsDirectory() { - if (pluginsDir == null || !pluginsDir.exists()) { + private List<Path> scanJarsInPluginsDirectory() { + if (pluginsDir == null || !Files.isDirectory(pluginsDir)) { return Collections.emptyList(); } - File[] matches = pluginsDir.listFiles(new FileFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File pathname) { - String n = pathname.getName(); - return (n.endsWith(".jar") && pathname.isFile()); + public boolean accept(Path entry) throws IOException { + return entry.getFileName().toString().endsWith(".jar") + && Files.isRegularFile(entry); } - }); - if (matches == null) { - ui.message("WARN: Cannot list %s", pluginsDir.getAbsolutePath()); + }; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(pluginsDir, filter)) { + return Ordering.natural().sortedCopy(paths); + } catch (IOException e) { + ui.message("WARN: Cannot list %s: %s", pluginsDir.toAbsolutePath(), + e.getMessage()); return Collections.emptyList(); } - Arrays.sort(matches, new Comparator<File>() { - @Override - public int compare(File o1, File o2) { - return o1.getName().compareTo(o2.getName()); - } - }); - return Arrays.asList(matches); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java index 72400a4..a714ac9 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java
@@ -25,9 +25,10 @@ import com.google.inject.Injector; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.jar.Attributes; @@ -56,10 +57,10 @@ pluginsDistribution.foreach(new PluginsDistribution.Processor() { @Override public void process(String pluginName, InputStream in) throws IOException { - File tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site); + Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site); String pluginVersion = getVersion(tmpPlugin); if (deleteTempPluginFile) { - tmpPlugin.delete(); + Files.delete(tmpPlugin); } result.add(new PluginData(pluginName, pluginVersion, tmpPlugin)); } @@ -110,37 +111,39 @@ for (PluginData plugin : plugins) { String pluginName = plugin.name; try { - final File tmpPlugin = plugin.pluginFile; + final Path tmpPlugin = plugin.pluginPath; if (!(initFlags.installPlugins.contains(pluginName) || ui.yesno(false, "Install plugin %s version %s", pluginName, plugin.version))) { - tmpPlugin.delete(); + Files.deleteIfExists(tmpPlugin); continue; } - final File p = new File(site.plugins_dir, plugin.name + ".jar"); - if (p.exists()) { + final Path p = site.plugins_dir.resolve(plugin.name + ".jar"); + if (Files.exists(p)) { final String installedPluginVersion = getVersion(p); if (!ui.yesno(false, "version %s is already installed, overwrite it", installedPluginVersion)) { - tmpPlugin.delete(); + Files.deleteIfExists(tmpPlugin); continue; } - if (!p.delete()) { + try { + Files.delete(p); + } catch (IOException e) { throw new IOException("Failed to delete plugin " + pluginName - + ": " + p.getAbsolutePath()); + + ": " + p.toAbsolutePath(), e); } } - if (!tmpPlugin.renameTo(p)) { + try { + Files.move(tmpPlugin, p); + } catch (IOException e) { throw new IOException("Failed to install plugin " + pluginName - + ": " + tmpPlugin.getAbsolutePath() + " -> " - + p.getAbsolutePath()); + + ": " + tmpPlugin.toAbsolutePath() + " -> " + + p.toAbsolutePath(), e); } } finally { - if (plugin.pluginFile.exists()) { - plugin.pluginFile.delete(); - } + Files.deleteIfExists(plugin.pluginPath); } } if (plugins.isEmpty()) { @@ -167,11 +170,11 @@ } } - private static String getVersion(final File plugin) throws IOException { - final JarFile jarFile = new JarFile(plugin); + private static String getVersion(Path plugin) throws IOException { + JarFile jarFile = new JarFile(plugin.toFile()); try { - final Manifest manifest = jarFile.getManifest(); - final Attributes main = manifest.getMainAttributes(); + Manifest manifest = jarFile.getManifest(); + Attributes main = manifest.getMainAttributes(); return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION); } finally { jarFile.close();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java index 51eaa22..5c7eefd 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java
@@ -25,6 +25,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; +import java.nio.file.Files; + /** Initialize the {@code sendemail} configuration section. */ @Singleton class InitSendEmail implements InitStep { @@ -54,7 +56,7 @@ true); String username = null; - if (site.gerrit_config.exists()) { + if (Files.exists(site.gerrit_config)) { username = sendemail.get("smtpUser"); } else if ((enc != null && enc != Encryption.NONE) || !isLocal(hostname)) { username = username();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java index ed18d73..c654c8d 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java
@@ -17,6 +17,7 @@ import static com.google.gerrit.common.FileUtil.chmod; import static com.google.gerrit.pgm.init.api.InitUtil.die; import static com.google.gerrit.pgm.init.api.InitUtil.hostname; +import static java.nio.file.Files.exists; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -29,9 +30,10 @@ import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; -import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code sshd} configuration section. */ @Singleton @@ -74,9 +76,9 @@ port = ui.readInt(port, "Listen on port"); sshd.set("listenAddress", SocketUtil.format(hostname, port)); - if (site.ssh_rsa.exists() || site.ssh_dsa.exists()) { + if (exists(site.ssh_rsa) || exists(site.ssh_dsa)) { libraries.bouncyCastleSSL.downloadRequired(); - } else if (!site.ssh_key.exists()) { + } else if (!exists(site.ssh_key)) { libraries.bouncyCastleSSL.downloadOptional(); } @@ -90,9 +92,9 @@ } private void generateSshHostKeys() throws InterruptedException, IOException { - if (!site.ssh_key.exists() // - && !site.ssh_rsa.exists() // - && !site.ssh_dsa.exists()) { + if (!exists(site.ssh_key) // + && !exists(site.ssh_rsa) // + && !exists(site.ssh_dsa)) { System.err.print("Generating SSH host key ..."); System.err.flush(); @@ -108,7 +110,7 @@ "-t", "rsa", // "-P", "", // "-C", comment, // - "-f", site.ssh_rsa.getAbsolutePath() // + "-f", site.ssh_rsa.toAbsolutePath().toString() // }).waitFor(); System.err.print(" dsa..."); @@ -118,7 +120,7 @@ "-t", "dsa", // "-P", "", // "-C", comment, // - "-f", site.ssh_dsa.getAbsolutePath() // + "-f", site.ssh_dsa.toAbsolutePath().toString() // }).waitFor(); } else { @@ -128,28 +130,34 @@ // short period of time. We try to reduce that risk by creating // the key within a temporary directory. // - final File tmpdir = new File(site.etc_dir, "tmp.sshkeygen"); - if (!tmpdir.mkdir()) { - throw die("Cannot create directory " + tmpdir); + Path tmpdir = site.etc_dir.resolve("tmp.sshkeygen"); + try { + Files.createDirectory(tmpdir); + } catch (IOException e) { + throw die("Cannot create directory " + tmpdir, e); } chmod(0600, tmpdir); - final File tmpkey = new File(tmpdir, site.ssh_key.getName()); - final SimpleGeneratorHostKeyProvider p; + Path tmpkey = tmpdir.resolve(site.ssh_key.getFileName().toString()); + SimpleGeneratorHostKeyProvider p; System.err.print(" rsa(simple)..."); System.err.flush(); p = new SimpleGeneratorHostKeyProvider(); - p.setPath(tmpkey.getAbsolutePath()); + p.setPath(tmpkey.toAbsolutePath().toString()); p.setAlgorithm("RSA"); p.loadKeys(); // forces the key to generate. chmod(0600, tmpkey); - if (!tmpkey.renameTo(site.ssh_key)) { - throw die("Cannot rename " + tmpkey + " to " + site.ssh_key); + try { + Files.move(tmpkey, site.ssh_key); + } catch (IOException e) { + throw die("Cannot rename " + tmpkey + " to " + site.ssh_key, e); } - if (!tmpdir.delete()) { - throw die("Cannot delete " + tmpdir); + try { + Files.delete(tmpdir); + } catch (IOException e) { + throw die("Cannot delete " + tmpdir, e); } } System.err.println(" done");
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java index 4bf1c88..00c7c58 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java
@@ -15,21 +15,19 @@ package com.google.gerrit.pgm.init; import com.google.common.base.Strings; -import com.google.common.io.Files; +import com.google.common.hash.Funnels; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteStreams; import com.google.gerrit.common.Die; import com.google.gerrit.common.IoUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; -import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.util.HttpSupport; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -38,15 +36,17 @@ import java.net.ProxySelector; import java.net.URISyntaxException; import java.net.URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; /** Get optional or required 3rd party library files into $site_path/lib. */ class LibraryDownloader { private final ConsoleUI ui; - private final File lib_dir; + private final Path lib_dir; private boolean required; private String name; @@ -55,7 +55,7 @@ private String remove; private List<LibraryDownloader> needs; private LibraryDownloader neededBy; - private File dst; + private Path dst; private boolean download; // download or copy private boolean exists; @@ -118,8 +118,8 @@ name = jarName; } - dst = new File(lib_dir, jarName); - if (dst.exists()) { + dst = lib_dir.resolve(jarName); + if (Files.exists(dst)) { exists = true; } else if (shouldGet()) { doGet(); @@ -158,8 +158,12 @@ } private void doGet() { - if (!lib_dir.exists() && !lib_dir.mkdirs()) { - throw new Die("Cannot create " + lib_dir); + if (!Files.exists(lib_dir)) { + try { + Files.createDirectories(lib_dir); + } catch (IOException e) { + throw new Die("Cannot create " + lib_dir, e); + } } try { @@ -171,7 +175,11 @@ } verifyFileChecksum(); } catch (IOException err) { - dst.delete(); + try { + Files.delete(dst); + } catch (IOException e) { + // Delete failed; leave alone. + } if (ui.isBatch()) { throw new Die("error: Cannot get " + jarUrl, err); @@ -186,13 +194,13 @@ System.err.println(); System.err.println("and save as:"); System.err.println(); - System.err.println(" " + dst.getAbsolutePath()); + System.err.println(" " + dst.toAbsolutePath()); System.err.println(); System.err.flush(); ui.waitForUser(); - if (dst.exists()) { + if (Files.exists(dst)) { verifyFileChecksum(); } else if (!ui.yesno(!required, "Continue without this library")) { @@ -200,7 +208,7 @@ } } - if (dst.exists()) { + if (Files.exists(dst)) { exists = true; IoUtil.loadJARs(dst); } @@ -208,131 +216,120 @@ private void removeStaleVersions() { if (!Strings.isNullOrEmpty(remove)) { - String[] names = lib_dir.list(new FilenameFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File dir, String name) { - return name.matches("^" + remove + "$"); + public boolean accept(Path entry) { + return entry.getFileName().toString() + .matches("^" + remove + "$"); } - }); - if (names != null) { - for (String old : names) { + }; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(lib_dir, filter)) { + for (Path p : paths) { + String old = p.getFileName().toString(); String bak = "." + old + ".backup"; ui.message("Renaming %s to %s", old, bak); - if (!new File(lib_dir, old).renameTo(new File(lib_dir, bak))) { - throw new Die("cannot rename " + old); + try { + Files.move(p, p.resolveSibling(bak)); + } catch (IOException e) { + throw new Die("cannot rename " + old, e); } } + } catch (IOException e) { + throw new Die("cannot remove stale library versions", e); } } } private void doGetByLocalCopy() throws IOException { System.err.print("Copying " + jarUrl + " ..."); - File f = url2file(jarUrl); - if (!f.exists()) { + Path p = url2file(jarUrl); + if (!Files.exists(p)) { StringBuilder msg = new StringBuilder() .append("\n") .append("Can not find the %s at this location: %s\n") .append("Please provide alternative URL"); - f = url2file(ui.readString(null, msg.toString(), name, jarUrl)); + p = url2file(ui.readString(null, msg.toString(), name, jarUrl)); } - Files.copy(f, dst); + Files.copy(p, dst); } - private static File url2file(final String urlString) throws IOException { + private static Path url2file(final String urlString) throws IOException { final URL url = new URL(urlString); try { - return new File(url.toURI()); + return Paths.get(url.toURI()); } catch (URISyntaxException e) { - return new File(url.getPath()); + return Paths.get(url.getPath()); } } private void doGetByHttp() throws IOException { System.err.print("Downloading " + jarUrl + " ..."); System.err.flush(); - try { - final ProxySelector proxySelector = ProxySelector.getDefault(); - final URL url = new URL(jarUrl); - final Proxy proxy = HttpSupport.proxyFor(proxySelector, url); - final HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy); - final InputStream in; - - switch (HttpSupport.response(c)) { - case HttpURLConnection.HTTP_OK: - in = c.getInputStream(); - break; - - case HttpURLConnection.HTTP_NOT_FOUND: - throw new FileNotFoundException(url.toString()); - - default: - throw new IOException(url.toString() + ": " + HttpSupport.response(c) - + " " + c.getResponseMessage()); - } - - try { - final OutputStream out = new FileOutputStream(dst); - try { - final byte[] buf = new byte[8192]; - int n; - while ((n = in.read(buf)) > 0) { - out.write(buf, 0, n); - } - } finally { - out.close(); - } - } finally { - in.close(); - } + try (InputStream in = openHttpStream(jarUrl); + OutputStream out = Files.newOutputStream(dst)) { + ByteStreams.copy(in, out); System.err.println(" OK"); System.err.flush(); } catch (IOException err) { - dst.delete(); + deleteDst(); System.err.println(" !! FAIL !!"); System.err.flush(); throw err; } } + private static InputStream openHttpStream(String urlStr) throws IOException { + ProxySelector proxySelector = ProxySelector.getDefault(); + URL url = new URL(urlStr); + Proxy proxy = HttpSupport.proxyFor(proxySelector, url); + HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy); + + switch (HttpSupport.response(c)) { + case HttpURLConnection.HTTP_OK: + return c.getInputStream(); + + case HttpURLConnection.HTTP_NOT_FOUND: + throw new FileNotFoundException(url.toString()); + + default: + throw new IOException(url.toString() + ": " + HttpSupport.response(c) + + " " + c.getResponseMessage()); + } + } + private void verifyFileChecksum() { - if (sha1 != null) { - try { - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - final FileInputStream in = new FileInputStream(dst); - try { - final byte[] buf = new byte[8192]; - int n; - while ((n = in.read(buf)) > 0) { - md.update(buf, 0, n); - } - } finally { - in.close(); - } + if (sha1 == null) { + return; + } + Hasher h = Hashing.sha1().newHasher(); + try (InputStream in = Files.newInputStream(dst); + OutputStream out = Funnels.asOutputStream(h)) { + ByteStreams.copy(in, out); + } catch (IOException e) { + deleteDst(); + throw new Die("cannot checksum " + dst, e); + } + if (sha1.equals(h.hash().toString())) { + System.err.println("Checksum " + dst.getFileName() + " OK"); + System.err.flush(); + } else if (ui.isBatch()) { + deleteDst(); + throw new Die(dst + " SHA-1 checksum does not match"); - if (sha1.equals(ObjectId.fromRaw(md.digest()).name())) { - System.err.println("Checksum " + dst.getName() + " OK"); - System.err.flush(); + } else if (!ui.yesno(null /* force an answer */, + "error: SHA-1 checksum does not match\n" + "Use %s anyway",// + dst.getFileName())) { + deleteDst(); + throw new Die("aborted by user"); + } + } - } else if (ui.isBatch()) { - dst.delete(); - throw new Die(dst + " SHA-1 checksum does not match"); - - } else if (!ui.yesno(null /* force an answer */, - "error: SHA-1 checksum does not match\n" + "Use %s anyway",// - dst.getName())) { - dst.delete(); - throw new Die("aborted by user"); - } - - } catch (IOException checksumError) { - dst.delete(); - throw new Die("cannot checksum " + dst, checksumError); - - } catch (NoSuchAlgorithmException checksumError) { - dst.delete(); - throw new Die("cannot checksum " + dst, checksumError); - } + private void deleteDst() { + try { + Files.delete(dst); + } catch (IOException e) { + System.err.println(" Failed to clean up lib: " + dst); } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java index 8926759..49877dc 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java
@@ -14,13 +14,13 @@ package com.google.gerrit.pgm.init; -import java.io.File; +import java.nio.file.Path; class SecureStoreInitData { - final File jarFile; + final Path jarFile; final String className; - SecureStoreInitData(File jar, String className) { + SecureStoreInitData(Path jar, String className) { this.className = className; this.jarFile = jar; }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java index 10c9bad..8a227ac 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -21,7 +21,7 @@ import static com.google.gerrit.pgm.init.api.InitUtil.savePublic; import static com.google.gerrit.pgm.init.api.InitUtil.version; -import com.google.common.io.Files; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.common.Nullable; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitFlags; @@ -35,8 +35,9 @@ import com.google.inject.Injector; import com.google.inject.TypeLiteral; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -53,7 +54,7 @@ public SitePathInitializer(final Injector injector, final ConsoleUI ui, final InitFlags flags, final SitePaths site, final Section.Factory sectionFactory, - final @Nullable SecureStoreInitData secureStoreInitData) { + @Nullable final SecureStoreInitData secureStoreInitData) { this.ui = ui; this.flags = flags; this.site = site; @@ -66,12 +67,10 @@ ui.header("Gerrit Code Review %s", version()); if (site.isNew) { - if (!ui.yesno(true, "Create '%s'", site.site_path.getCanonicalPath())) { + if (!ui.yesno(true, "Create '%s'", site.site_path.toAbsolutePath())) { throw die("aborted by user"); } - if (!site.site_path.isDirectory() && !site.site_path.mkdirs()) { - throw die("Cannot make directory " + site.site_path); - } + FileUtil.mkdirsOrDie(site.site_path, "Cannot make directory"); flags.deleteOnFailure = true; } @@ -132,7 +131,8 @@ private void saveSecureStore() throws IOException { if (secureStoreInitData != null) { - File dst = new File(site.lib_dir, secureStoreInitData.jarFile.getName()); + Path dst = + site.lib_dir.resolve(secureStoreInitData.jarFile.getFileName()); Files.copy(secureStoreInitData.jarFile, dst); Section gerritSection = sectionFactory.get("gerrit", null); gerritSection.set("secureStoreClass", secureStoreInitData.className); @@ -140,7 +140,7 @@ } private void extractMailExample(String orig) throws Exception { - File ex = new File(site.mail_dir, orig + ".example"); + Path ex = site.mail_dir.resolve(orig + ".example"); extract(ex, OutgoingEmail.class, orig); chmod(0444, ex); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java index 8c13540..21cd3c8 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java
@@ -30,13 +30,14 @@ import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.storage.file.FileBasedConfig; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Map; import java.util.Properties; @@ -64,8 +65,8 @@ private final FileBasedConfig cfg; private final SecureStore sec; - private final File site_path; - private final File etc_dir; + private final Path site_path; + private final Path etc_dir; private final Section.Factory sections; @Inject @@ -82,7 +83,7 @@ boolean isNeedUpgrade() { for (String name : etcFiles) { - if (new File(site_path, name).exists()) { + if (Files.exists(site_path.resolve(name))) { return true; } } @@ -95,19 +96,21 @@ return; } - if (!ui.yesno(true, "Upgrade '%s'", site_path.getCanonicalPath())) { + if (!ui.yesno(true, "Upgrade '%s'", site_path.toAbsolutePath())) { throw die("aborted by user"); } for (String name : etcFiles) { - final File src = new File(site_path, name); - final File dst = new File(etc_dir, name); - if (src.exists()) { - if (dst.exists()) { + Path src = site_path.resolve(name); + Path dst = etc_dir.resolve(name); + if (Files.exists(src)) { + if (Files.exists(dst)) { throw die("File " + src + " would overwrite " + dst); } - if (!src.renameTo(dst)) { - throw die("Cannot rename " + src + " to " + dst); + try { + Files.move(src, dst); + } catch (IOException e) { + throw die("Cannot rename " + src + " to " + dst, e); } } } @@ -256,23 +259,18 @@ private Properties readGerritServerProperties() throws IOException { final Properties srvprop = new Properties(); final String name = System.getProperty("GerritServer"); - File path; + Path path; if (name != null) { - path = new File(name); + path = Paths.get(name); } else { - path = new File(site_path, "GerritServer.properties"); - if (!path.exists()) { - path = new File("GerritServer.properties"); + path = site_path.resolve("GerritServer.properties"); + if (!Files.exists(path)) { + path = Paths.get("GerritServer.properties"); } } - if (path.exists()) { - try { - final InputStream in = new FileInputStream(path); - try { - srvprop.load(in); - } finally { - in.close(); - } + if (Files.exists(path)) { + try (InputStream in = Files.newInputStream(path)) { + srvprop.load(in); } catch (IOException e) { throw new IOException("Cannot read " + name, e); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java index f45a970..10d93ee 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -40,6 +40,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; public class AllProjectsConfig extends VersionedMetaData { @@ -68,11 +69,11 @@ } private File getPath() { - File basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath")); + Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath")); if (basePath == null) { throw new IllegalStateException("gerrit.basePath must be configured"); } - return FileKey.resolve(new File(basePath, project), FS.DETECTED); + return FileKey.resolve(basePath.resolve(project).toFile(), FS.DETECTED); } public AllProjectsConfig load() throws IOException, ConfigInvalidException {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java index 2a8155e..c8f03cc 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java
@@ -47,11 +47,11 @@ @Inject public InitFlags(final SitePaths site, final SecureStore secureStore, - final @InstallPlugins List<String> installPlugins) throws IOException, + @InstallPlugins final List<String> installPlugins) throws IOException, ConfigInvalidException { sec = secureStore; this.installPlugins = installPlugins; - cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); cfg.load(); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java index 881208d..904af2f 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java
@@ -16,14 +16,15 @@ import static com.google.gerrit.common.FileUtil.modified; +import com.google.common.io.ByteStreams; import com.google.gerrit.common.Die; import org.eclipse.jgit.internal.storage.file.LockFile; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.SystemReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -33,7 +34,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; -import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Arrays; /** Utility functions to help initialize a site. */ public class InitUtil { @@ -51,9 +55,18 @@ } } - public static void mkdir(final File path) { - if (!path.isDirectory() && !path.mkdir()) { - throw die("Cannot make directory " + path); + public static void mkdir(File file) { + mkdir(file.toPath()); + } + + public static void mkdir(Path path) { + if (Files.isDirectory(path)) { + return; + } + try { + Files.createDirectory(path); + } catch (IOException e) { + throw die("Cannot make directory " + path, e); } } @@ -109,12 +122,11 @@ return name; } - public static void extract(final File dst, final Class<?> sibling, - final String name) throws IOException { + public static void extract(Path dst, Class<?> sibling, String name) + throws IOException { try (InputStream in = open(sibling, name)) { if (in != null) { - ByteBuffer buf = IO.readWholeStream(in, 8192); - copy(dst, buf); + copy(dst, ByteStreams.toByteArray(in)); } } } @@ -136,35 +148,28 @@ return in; } - public static void copy(final File dst, final ByteBuffer buf) + public static void copy(Path dst, byte[] buf) throws FileNotFoundException, IOException { // If the file already has the content we want to put there, // don't attempt to overwrite the file. // - try { - if (buf.equals(ByteBuffer.wrap(IO.readFully(dst)))) { + try (InputStream in = Files.newInputStream(dst)) { + if (Arrays.equals(buf, ByteStreams.toByteArray(in))) { return; } - } catch (FileNotFoundException notFound) { + } catch (NoSuchFileException notFound) { // Fall through and write the file. } - dst.getParentFile().mkdirs(); - LockFile lf = new LockFile(dst, FS.DETECTED); + Files.createDirectories(dst.getParent()); + LockFile lf = new LockFile(dst.toFile(), FS.DETECTED); if (!lf.lock()) { throw new IOException("Cannot lock " + dst); } try { - final OutputStream out = lf.getOutputStream(); - try { - final byte[] tmp = new byte[4096]; - while (0 < buf.remaining()) { - int n = Math.min(buf.remaining(), tmp.length); - buf.get(tmp, 0, n); - out.write(tmp, 0, n); - } - } finally { - out.close(); + try (InputStream in = new ByteArrayInputStream(buf); + OutputStream out = lf.getOutputStream()) { + ByteStreams.copy(in, out); } if (!lf.commit()) { throw new IOException("Cannot commit " + dst);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java index fbd8ecd..52b0daa 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java
@@ -20,7 +20,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Set; @@ -68,10 +68,9 @@ flags.cfg.setStringList(section, subsection, name, all); } - } else if (all.size() == 0) { } else if (all.size() == 1) { flags.cfg.unset(section, subsection, name); - } else { + } else if (all.size() != 0) { all.remove(0); flags.cfg.setStringList(section, subsection, name, all); } @@ -106,7 +105,7 @@ return nv; } - public File path(final String title, final String name, final String defValue) { + public Path path(final String title, final String name, final String defValue) { return site.resolve(string(title, name, defValue)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/rules/PrologCompiler.java similarity index 80% rename from gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java rename to gerrit-pgm/src/main/java/com/google/gerrit/pgm/rules/PrologCompiler.java index 4724bc2..ca8b183 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/rules/PrologCompiler.java
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.rules; +package com.google.gerrit.pgm.rules; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.Version; @@ -22,8 +22,8 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import com.googlecode.prolog_cafe.compiler.CompileException; import com.googlecode.prolog_cafe.compiler.Compiler; +import com.googlecode.prolog_cafe.exceptions.CompileException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.Config; @@ -35,8 +35,11 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -67,14 +70,14 @@ NO_RULES, COMPILED } - private final File ruleDir; + private final Path ruleDir; private final Repository git; @Inject PrologCompiler(@GerritServerConfig Config config, SitePaths site, @Assisted Repository gitRepository) { - File cacheDir = site.resolve(config.getString("cache", null, "directory")); - ruleDir = cacheDir != null ? new File(cacheDir, "rules") : null; + Path cacheDir = site.resolve(config.getString("cache", null, "directory")); + ruleDir = cacheDir != null ? cacheDir.resolve("rules") : null; git = gitRepository; } @@ -93,9 +96,7 @@ if (ruleDir == null) { throw new CompileException("Caching not enabled"); } - if (!ruleDir.isDirectory() && !ruleDir.mkdir()) { - throw new IOException("Cannot create " + ruleDir); - } + Files.createDirectories(ruleDir); File tempDir = File.createTempFile("GerritCodeReview_", ".rulec"); if (!tempDir.delete() || !tempDir.mkdir()) { @@ -111,9 +112,9 @@ compileProlog(rulesId, tempDir); compileJava(tempDir); - File jarFile = new File(ruleDir, "rules-" + rulesId.getName() + ".jar"); + Path jarPath = ruleDir.resolve("rules-" + rulesId.getName() + ".jar"); List<String> classFiles = getRelativePaths(tempDir, ".class"); - createJar(jarFile, classFiles, tempDir, metaConfig, rulesId); + createJar(jarPath, classFiles, tempDir, metaConfig, rulesId); return Status.COMPILED; } finally { @@ -222,51 +223,51 @@ } /** Takes compiled prolog .class files, puts them into the jar file. */ - private void createJar(File archiveFile, List<String> toBeJared, + private void createJar(Path archiveFile, List<String> toBeJared, File tempDir, ObjectId metaConfig, ObjectId rulesId) throws IOException { long now = TimeUtil.nowMs(); - File tmpjar = File.createTempFile(".rulec_", ".jar", archiveFile.getParentFile()); - try { - Manifest mf = new Manifest(); - mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - mf.getMainAttributes().putValue("Built-by", "Gerrit Code Review " + Version.getVersion()); - if (git.getDirectory() != null) { - mf.getMainAttributes().putValue("Source-Repository", git.getDirectory().getPath()); - } - mf.getMainAttributes().putValue("Source-Commit", metaConfig.name()); - mf.getMainAttributes().putValue("Source-Blob", rulesId.name()); + Manifest mf = new Manifest(); + mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + mf.getMainAttributes().putValue("Built-by", "Gerrit Code Review " + Version.getVersion()); + if (git.getDirectory() != null) { + mf.getMainAttributes().putValue("Source-Repository", git.getDirectory().getPath()); + } + mf.getMainAttributes().putValue("Source-Commit", metaConfig.name()); + mf.getMainAttributes().putValue("Source-Blob", rulesId.name()); - try (FileOutputStream stream = new FileOutputStream(tmpjar); - JarOutputStream out = new JarOutputStream(stream, mf)) { - byte buffer[] = new byte[10240]; - for (String path : toBeJared) { - JarEntry jarAdd = new JarEntry(path); - File f = new File(tempDir, path); - jarAdd.setTime(now); - out.putNextEntry(jarAdd); - if (f.isFile()) { - FileInputStream in = new FileInputStream(f); - try { - while (true) { - int nRead = in.read(buffer, 0, buffer.length); - if (nRead <= 0) { - break; - } - out.write(buffer, 0, nRead); + Path tmpjar = + Files.createTempFile(archiveFile.getParent(), ".rulec_", ".jar"); + try (OutputStream stream = Files.newOutputStream(tmpjar); + JarOutputStream out = new JarOutputStream(stream, mf)) { + byte[] buffer = new byte[10240]; + // TODO: fixify this loop + for (String path : toBeJared) { + JarEntry jarAdd = new JarEntry(path); + File f = new File(tempDir, path); + jarAdd.setTime(now); + out.putNextEntry(jarAdd); + if (f.isFile()) { + FileInputStream in = new FileInputStream(f); + try { + while (true) { + int nRead = in.read(buffer, 0, buffer.length); + if (nRead <= 0) { + break; } - } finally { - in.close(); + out.write(buffer, 0, nRead); } + } finally { + in.close(); } - out.closeEntry(); } + out.closeEntry(); } + } - if (!tmpjar.renameTo(archiveFile)) { - throw new IOException("Cannot replace " + archiveFile); - } - } finally { - tmpjar.delete(); + try { + Files.move(tmpjar, archiveFile); + } catch (IOException e) { + throw new IOException("Cannot replace " + archiveFile, e); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java index 23983b7..60d4acc 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -87,6 +87,7 @@ install(reviewDbModule); install(new DiffExecutorModule()); install(PatchListCacheImpl.module()); + // Plugins are not loaded and we're just running through each change // once, so don't worry about cache removal. bind(new TypeLiteral<DynamicSet<CacheRemovalListener>>() {})
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java index a766d1e..ee0a0e6 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
@@ -14,22 +14,26 @@ package com.google.gerrit.pgm.util; -import com.google.gerrit.common.Die; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.util.SystemLog; +import net.logstash.log4j.JSONEventLayoutV1; + import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; +import org.eclipse.jgit.lib.Config; -import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; public class ErrorLogFile { static final String LOG_NAME = "error_log"; + static final String JSON_SUFFIX = ".json"; public static void errorOnlyConsole() { LogManager.resetConfiguration(); @@ -47,14 +51,12 @@ root.addAppender(dst); } - public static LifecycleListener start(final File sitePath) - throws FileNotFoundException { - final File logdir = new SitePaths(sitePath).logs_dir; - if (!logdir.exists() && !logdir.mkdirs()) { - throw new Die("Cannot create log directory: " + logdir); - } + public static LifecycleListener start(final Path sitePath, final Config config) + throws IOException { + Path logdir = FileUtil.mkdirsOrDie(new SitePaths(sitePath).logs_dir, + "Cannot create log directory"); if (SystemLog.shouldConfigure()) { - initLogSystem(logdir); + initLogSystem(logdir, config); } return new LifecycleListener() { @@ -69,10 +71,21 @@ }; } - private static void initLogSystem(final File logdir) { + private static void initLogSystem(Path logdir, Config config) { final Logger root = LogManager.getRootLogger(); root.removeAllAppenders(); - root.addAppender(SystemLog.createAppender(logdir, LOG_NAME, - new PatternLayout("[%d] %-5p %c %x: %m%n"))); + + boolean json = config.getBoolean("log", "jsonLogging", false); + boolean text = config.getBoolean("log", "textLogging", true) || !json; + + if (text) { + root.addAppender(SystemLog.createAppender(logdir, LOG_NAME, + new PatternLayout("[%d] %-5p %c %x: %m%n"))); + } + + if (json) { + root.addAppender(SystemLog.createAppender(logdir, LOG_NAME + JSON_SUFFIX, + new JSONEventLayoutV1())); + } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java index db74ac3..1107208 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java
@@ -16,6 +16,7 @@ import static java.util.concurrent.TimeUnit.HOURS; +import com.google.common.io.ByteStreams; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.server.config.SitePaths; @@ -25,12 +26,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.zip.GZIPOutputStream; /** Compresses the old error logs. */ @@ -65,76 +66,78 @@ } } - private final File logs_dir; + private final Path logs_dir; @Inject LogFileCompressor(final SitePaths site) { logs_dir = resolve(site.logs_dir); } - private static File resolve(final File logs_dir) { + private static Path resolve(Path p) { try { - return logs_dir.getCanonicalFile(); + return p.toRealPath().normalize(); } catch (IOException e) { - return logs_dir.getAbsoluteFile(); + return p.toAbsolutePath().normalize(); } } @Override public void run() { - final File[] list = logs_dir.listFiles(); - if (list == null) { + if (!Files.isDirectory(logs_dir)) { return; } - - for (final File entry : list) { - if (!isLive(entry) && !isCompressed(entry) && isLogFile(entry)) { - compress(entry); + try (DirectoryStream<Path> list = Files.newDirectoryStream(logs_dir)) { + for (Path entry : list) { + if (!isLive(entry) && !isCompressed(entry) && isLogFile(entry)) { + compress(entry); + } } + } catch (IOException e) { + log.error("Error listing logs to compress in " + logs_dir, e); } } - private boolean isLive(final File entry) { - final String name = entry.getName(); + private boolean isLive(Path entry) { + String name = entry.getFileName().toString(); return name.endsWith("_log") || name.endsWith(".log") || name.endsWith(".run") || name.endsWith(".pid"); } - private boolean isCompressed(final File entry) { - final String name = entry.getName(); + private boolean isCompressed(Path entry) { + String name = entry.getFileName().toString(); return name.endsWith(".gz") // || name.endsWith(".zip") // || name.endsWith(".bz2"); } - private boolean isLogFile(final File entry) { - return entry.isFile(); + private boolean isLogFile(Path entry) { + return Files.isRegularFile(entry); } - private void compress(final File src) { - final File dir = src.getParentFile(); - final File dst = new File(dir, src.getName() + ".gz"); - final File tmp = new File(dir, ".tmp." + src.getName()); + private void compress(Path src) { + Path dst = src.resolveSibling(src.getFileName() + ".gz"); + Path tmp = src.resolveSibling(".tmp." + src.getFileName()); try { - try (InputStream in = new FileInputStream(src); - FileOutputStream fo = new FileOutputStream(tmp); - OutputStream out = new GZIPOutputStream(fo)) { - final byte[] buf = new byte[2048]; - int n; - while (0 < (n = in.read(buf))) { - out.write(buf, 0, n); - } - tmp.setReadOnly(); + try (InputStream in = Files.newInputStream(src); + OutputStream out = new GZIPOutputStream(Files.newOutputStream(tmp))) { + ByteStreams.copy(in, out); } - if (!tmp.renameTo(dst)) { - throw new IOException("Cannot rename " + tmp + " to " + dst); + tmp.toFile().setReadOnly(); + try { + Files.move(tmp, dst); + } catch (IOException e) { + throw new IOException("Cannot rename " + tmp + " to " + dst, e); } - src.delete(); + Files.delete(src); } catch (IOException e) { log.error("Cannot compress " + src, e); - tmp.delete(); + try { + Files.deleteIfExists(tmp); + } catch (IOException e2) { + log.warn("Failed to delete temporary log file " + tmp, e2); + } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java index c713b79..048c2ee 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
@@ -24,14 +24,14 @@ import org.eclipse.jgit.lib.Config; -import java.io.File; +import java.nio.file.Path; import javax.sql.DataSource; /** Loads the site library if not yet loaded. */ @Singleton public class SiteLibraryBasedDataSourceProvider extends DataSourceProvider { - private final File libdir; + private final Path libdir; private boolean init; @Inject
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java index 9763b1a..6eb313e 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -50,8 +50,10 @@ import org.eclipse.jgit.lib.Config; import org.kohsuke.args4j.Option; -import java.io.File; import java.lang.annotation.Annotation; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; @@ -61,30 +63,30 @@ public abstract class SiteProgram extends AbstractProgram { @Option(name = "--site-path", aliases = {"-d"}, usage = "Local directory containing site data") - private File sitePath = new File("."); + private void setSitePath(String path) { + sitePath = Paths.get(path); + } protected Provider<DataSource> dsProvider; + private Path sitePath = Paths.get("."); + protected SiteProgram() { } - protected SiteProgram(File sitePath, final Provider<DataSource> dsProvider) { + protected SiteProgram(Path sitePath, final Provider<DataSource> dsProvider) { this.sitePath = sitePath; this.dsProvider = dsProvider; } /** @return the site path specified on the command line. */ - protected File getSitePath() { - File path = sitePath.getAbsoluteFile(); - if (".".equals(path.getName())) { - path = path.getParentFile(); - } - return path; + protected Path getSitePath() { + return sitePath; } /** Ensures we are running inside of a valid site, otherwise throws a Die. */ protected void mustHaveValidSite() throws Die { - if (!new File(new File(getSitePath(), "etc"), "gerrit.config").exists()) { + if (!Files.exists(sitePath.resolve("etc").resolve("gerrit.config"))) { throw die("not a Gerrit site: '" + getSitePath() + "'\n" + "Perhaps you need to run init first?"); } @@ -92,13 +94,13 @@ /** @return provides database connectivity and site path. */ protected Injector createDbInjector(final DataSourceProvider.Context context) { - final File sitePath = getSitePath(); + final Path sitePath = getSitePath(); final List<Module> modules = new ArrayList<>(); Module sitePathModule = new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath); bind(String.class).annotatedWith(SecureStoreClassName.class) .toProvider(Providers.of(getConfiguredSecureStoreClass())); } @@ -198,7 +200,7 @@ modules.add(new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath()); } }); modules.add(new GerritServerConfigModule());
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java index 4d7370b..150309e 100644 --- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java +++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java
@@ -16,11 +16,11 @@ import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; public abstract class InitTestCase extends LocalDiskRepositoryTestCase { - protected File newSitePath() throws IOException { - return new File(createWorkRepository().getWorkTree(), "test_site"); + protected Path newSitePath() throws IOException { + return createWorkRepository().getWorkTree().toPath().resolve("test_site"); } }
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java index a37c97d..2198788 100644 --- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java +++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
@@ -25,13 +25,12 @@ import org.junit.Test; -import java.io.File; -import java.io.FileNotFoundException; +import java.nio.file.Paths; public class LibrariesTest { @Test - public void testCreate() throws FileNotFoundException { - final SitePaths site = new SitePaths(new File(".")); + public void testCreate() throws Exception { + final SitePaths site = new SitePaths(Paths.get(".")); final ConsoleUI ui = createStrictMock(ConsoleUI.class); replay(ui);
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java index 720d108..dc7ce59 100644 --- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java +++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
@@ -14,6 +14,7 @@ package com.google.gerrit.pgm.init; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.easymock.EasyMock.createStrictMock; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; @@ -24,6 +25,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.common.io.ByteStreams; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitFlags; import com.google.gerrit.pgm.init.api.Section; @@ -34,13 +37,12 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.IO; import org.junit.Test; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.Writer; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -49,23 +51,17 @@ @Test public void testUpgrade() throws IOException, ConfigInvalidException { - final File p = newSitePath(); + final Path p = newSitePath(); final SitePaths site = new SitePaths(p); assertTrue(site.isNew); - assertTrue(site.site_path.mkdir()); - assertTrue(site.etc_dir.mkdir()); + FileUtil.mkdirsOrDie(site.etc_dir, "Failed to create"); for (String n : UpgradeFrom2_0_x.etcFiles) { - Writer w = new FileWriter(new File(p, n)); - try { - w.write("# " + n + "\n"); - } finally { - w.close(); - } + Files.write(p.resolve(n), ("# " + n + "\n").getBytes(UTF_8)); } FileBasedConfig old = - new FileBasedConfig(new File(p, "gerrit.config"), FS.DETECTED); + new FileBasedConfig(p.resolve("gerrit.config").toFile(), FS.DETECTED); old.setString("ldap", null, "username", "ldap.user"); old.setString("ldap", null, "password", "ldap.s3kr3t"); @@ -85,8 +81,11 @@ } }; - expect(ui.yesno(eq(true), eq("Upgrade '%s'"), eq(p.getCanonicalPath()))) - .andReturn(true); + expect(ui.yesno( + eq(true), + eq("Upgrade '%s'"), + eq(p.toAbsolutePath().normalize()))) + .andReturn(true); replay(ui); UpgradeFrom2_0_x u = new UpgradeFrom2_0_x(site, flags, ui, sections); @@ -96,13 +95,17 @@ verify(ui); for (String n : UpgradeFrom2_0_x.etcFiles) { - if ("gerrit.config".equals(n)) continue; - if ("secure.config".equals(n)) continue; - assertEquals("# " + n + "\n",// - new String(IO.readFully(new File(site.etc_dir, n)), "UTF-8")); + if ("gerrit.config".equals(n) || "secure.config".equals(n)) { + continue; + } + try (InputStream in = Files.newInputStream(site.etc_dir.resolve(n))) { + assertEquals("# " + n + "\n", + new String(ByteStreams.toByteArray(in), UTF_8)); + } } - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = + new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); cfg.load(); assertEquals("email.user", cfg.getString("sendemail", null, "smtpUser"));
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml index 17cf79a..eb2ec31 100644 --- a/gerrit-plugin-api/pom.xml +++ b/gerrit-plugin-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-api</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin API</name> <description>API for Gerrit Plugins</description>
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml index 8f9684a..1da5d51 100644 --- a/gerrit-plugin-archetype/pom.xml +++ b/gerrit-plugin-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-archetype</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Plugin Archetype</name> <description>Maven Archetype for Gerrit Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs index d4218a5..7397758 100644 --- a/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs +++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -1,10 +1,9 @@ -#Wed Jul 29 11:31:38 PDT 2009 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Google Format formatter_settings_version=11 org.eclipse.jdt.ui.ignorelowercasenames=true -org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax; +org.eclipse.jdt.ui.importorder=\#;com.google;com;dk;eu;junit;net;org;java;javax; org.eclipse.jdt.ui.ondemandthreshold=99 org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/gerrit-plugin-gwt-archetype/pom.xml b/gerrit-plugin-gwt-archetype/pom.xml index f384bc1..bd4d738 100644 --- a/gerrit-plugin-gwt-archetype/pom.xml +++ b/gerrit-plugin-gwt-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-gwt-archetype</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Web UI GWT Plugin Archetype</name> <description>Maven Archetype for Gerrit Web UI GWT Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs index d4218a5..7397758 100644 --- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs +++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -1,10 +1,9 @@ -#Wed Jul 29 11:31:38 PDT 2009 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Google Format formatter_settings_version=11 org.eclipse.jdt.ui.ignorelowercasenames=true -org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax; +org.eclipse.jdt.ui.importorder=\#;com.google;com;dk;eu;junit;net;org;java;javax; org.eclipse.jdt.ui.ondemandthreshold=99 org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml index e4fc192..fa261be 100644 --- a/gerrit-plugin-gwtui/pom.xml +++ b/gerrit-plugin-gwtui/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-gwtui</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin GWT UI</name> <description>Common Classes for Gerrit GWT UI Plugins</description>
diff --git a/gerrit-plugin-js-archetype/pom.xml b/gerrit-plugin-js-archetype/pom.xml index 559d9de..11d4d83 100644 --- a/gerrit-plugin-js-archetype/pom.xml +++ b/gerrit-plugin-js-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-js-archetype</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Web UI JavaScript Plugin Archetype</name> <description>Maven Archetype for Gerrit Web UI JavaScript Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs index d4218a5..7397758 100644 --- a/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs +++ b/gerrit-plugin-js-archetype/src/main/resources/archetype-resources/.settings/org.eclipse.jdt.ui.prefs
@@ -1,10 +1,9 @@ -#Wed Jul 29 11:31:38 PDT 2009 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Google Format formatter_settings_version=11 org.eclipse.jdt.ui.ignorelowercasenames=true -org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax; +org.eclipse.jdt.ui.importorder=\#;com.google;com;dk;eu;junit;net;org;java;javax; org.eclipse.jdt.ui.ondemandthreshold=99 org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml index 48591f8..fd88f6c 100644 --- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml +++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml
@@ -14,10 +14,9 @@ limitations under the License. --> <module> - <replace-with class='com.google.gerrit.prettify.client.PrivateScopeImplIE6'> + <replace-with class='com.google.gerrit.prettify.client.PrivateScopeImplIE8'> <when-type-is class='com.google.gerrit.prettify.client.PrivateScopeImpl'/> <any> - <when-property-is name="user.agent" value="ie6" /> <when-property-is name="user.agent" value="ie8" /> </any> </replace-with>
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE6.java b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE8.java similarity index 90% rename from gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE6.java rename to gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE8.java index abb4e15..0496d91 100644 --- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE6.java +++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrivateScopeImplIE8.java
@@ -16,8 +16,8 @@ import com.google.gwt.core.client.JavaScriptObject; -/** IE6 requires us to initialize the document before we can use it. */ -public class PrivateScopeImplIE6 extends PrivateScopeImpl { +/** MSIE requires us to initialize the document before we can use it. */ +public class PrivateScopeImplIE8 extends PrivateScopeImpl { private JavaScriptObject context; @Override
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/EditList.java b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/EditList.java index 6a42a8e..ec5c78d8 100644 --- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/EditList.java +++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/EditList.java
@@ -68,8 +68,9 @@ private int findCombinedEnd(final int i) { int end = i + 1; - while (end < edits.size() && (combineA(end) || combineB(end))) + while (end < edits.size() && (combineA(end) || combineB(end))) { end++; + } return end - 1; }
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/SparseFileContent.java b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/SparseFileContent.java index b6e3bf9..a57146f 100644 --- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/SparseFileContent.java +++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/common/SparseFileContent.java
@@ -130,10 +130,11 @@ return size(); } - if (idx < cur.base) + if (idx < cur.base) { high = mid; - else + } else { low = mid + 1; + } } while (low < high); return size(); @@ -183,10 +184,11 @@ currentRangeIdx = mid; return cur.get(idx); } - if (idx < cur.base) + if (idx < cur.base) { high = mid; - else + } else { low = mid + 1; + } } while (low < high); return null; }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java index d4fb311..2750380 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -14,7 +14,7 @@ package com.google.gerrit.reviewdb.client; -import static com.google.gerrit.reviewdb.client.RefNames.REFS_USER; +import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS; import com.google.gwtorm.client.Column; import com.google.gwtorm.client.IntKey; @@ -111,8 +111,8 @@ if (name == null) { return null; } - if (name.startsWith(REFS_USER)) { - return fromRefPart(name.substring(REFS_USER.length())); + if (name.startsWith(REFS_USERS)) { + return fromRefPart(name.substring(REFS_USERS.length())); } return null; }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Branch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Branch.java index e1a15e5..3f04306 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Branch.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Branch.java
@@ -19,9 +19,6 @@ /** Line of development within a {@link Project}. */ public final class Branch { - public static final String R_HEADS = "refs/heads/"; - public static final String R_REFS = "refs/"; - /** Branch name key */ public static class NameKey extends StringKey<Project.NameKey> { private static final long serialVersionUID = 1L; @@ -36,9 +33,9 @@ projectName = new Project.NameKey(); } - public NameKey(final Project.NameKey proj, final String n) { + public NameKey(final Project.NameKey proj, final String branchName) { projectName = proj; - branchName = n; + set(branchName); } @Override @@ -48,7 +45,7 @@ @Override protected void set(String newValue) { - branchName = newValue; + branchName = RefNames.fullName(newValue); } @Override @@ -57,15 +54,7 @@ } public String getShortName() { - final String n = get(); - - // Git style branches will tend to start with "refs/heads/". - // - if (n.startsWith(R_HEADS)) { - return n.substring(R_HEADS.length()); - } - - return n; + return RefNames.shortName(get()); } }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java index 072982a..4154913 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java
@@ -17,6 +17,10 @@ /** Constants and utilities for Gerrit-specific ref names. */ public class RefNames { + public static final String REFS = "refs/"; + + public static final String REFS_HEADS = "refs/heads/"; + public static final String REFS_CHANGES = "refs/changes/"; /** Note tree listing commits we refuse {@code refs/meta/reject-commits} */ @@ -26,7 +30,10 @@ public static final String REFS_CONFIG = "refs/meta/config"; /** Preference settings for a user {@code refs/users} */ - public static final String REFS_USER = "refs/users/"; + public static final String REFS_USERS = "refs/users/"; + + /** Default user preference settings */ + public static final String REFS_USERS_DEFAULT = RefNames.REFS_USERS + "default"; /** Configurations of project-specific dashboards (canned search queries). */ public static final String REFS_DASHBOARDS = "refs/meta/dashboards/"; @@ -48,9 +55,19 @@ /** Suffix of a meta ref in the notedb. */ public static final String META_SUFFIX = "/meta"; + public static String fullName(String ref) { + return (ref.startsWith(REFS) ? "" : REFS_HEADS) + ref; + } + + public static final String shortName(String ref) { + return ref.startsWith(REFS_HEADS) + ? ref.substring(REFS_HEADS.length()) + : ref; + } + public static String refsUsers(Account.Id accountId) { StringBuilder r = new StringBuilder(); - r.append(REFS_USER); + r.append(REFS_USERS); int account = accountId.get(); int m = account % 100; if (m < 10) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RevId.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RevId.java index 1a64ee7..a73fe5d 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RevId.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RevId.java
@@ -64,4 +64,9 @@ public boolean equals(Object o) { return (o instanceof RevId) && id.equals(((RevId) o).id); } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + id + "}"; + } }
diff --git a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java index 33da24a..4bf1d81 100644 --- a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java +++ b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java
@@ -14,10 +14,7 @@ package com.google.gerrit.reviewdb.client; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import org.junit.Test; @@ -63,13 +60,13 @@ } private static void assertRef(int changeId, int psId, String refName) { - assertTrue(PatchSet.isRef(refName)); - assertEquals(new PatchSet.Id(new Change.Id(changeId), psId), - PatchSet.Id.fromRef(refName)); + assertThat(PatchSet.isRef(refName)).isTrue(); + assertThat(PatchSet.Id.fromRef(refName)) + .isEqualTo(new PatchSet.Id(new Change.Id(changeId), psId)); } private static void assertNotRef(String refName) { - assertFalse(PatchSet.isRef(refName)); - assertNull(PatchSet.Id.fromRef(refName)); + assertThat(PatchSet.isRef(refName)).isFalse(); + assertThat(PatchSet.Id.fromRef(refName)).isNull(); } }
diff --git a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/RefNamesTest.java b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/RefNamesTest.java new file mode 100644 index 0000000..b0981a7 --- /dev/null +++ b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/RefNamesTest.java
@@ -0,0 +1,50 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.reviewdb.client; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; + +public class RefNamesTest { + private final Account.Id accountId = new Account.Id(1011123); + private final Change.Id changeId = new Change.Id(67473); + private final PatchSet.Id psId = new PatchSet.Id(changeId, 42); + + @Test + public void fullName() throws Exception { + assertThat(RefNames.fullName("refs/meta/config")).isEqualTo("refs/meta/config"); + assertThat(RefNames.fullName("refs/heads/master")).isEqualTo("refs/heads/master"); + assertThat(RefNames.fullName("master")).isEqualTo("refs/heads/master"); + assertThat(RefNames.fullName("refs/tags/v1.0")).isEqualTo("refs/tags/v1.0"); + } + + @Test + public void refsUsers() throws Exception { + assertThat(RefNames.refsUsers(accountId)).isEqualTo("refs/users/23/1011123"); + } + + @Test + public void refsDraftComments() throws Exception { + assertThat(RefNames.refsDraftComments(accountId, changeId)) + .isEqualTo("refs/draft-comments/23/1011123-67473"); + } + + @Test + public void refsEdit() throws Exception { + assertThat(RefNames.refsEdit(accountId, changeId, psId)) + .isEqualTo("refs/users/23/1011123/edit-67473/42"); + } +}
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK index 7202dc3..5cda4dd 100644 --- a/gerrit-server/BUCK +++ b/gerrit-server/BUCK
@@ -48,6 +48,7 @@ '//lib/antlr:java_runtime', '//lib/auto:auto-value', '//lib/commons:codec', + '//lib/commons:compress', '//lib/commons:dbcp', '//lib/commons:lang', '//lib/commons:net', @@ -59,6 +60,7 @@ '//lib/jgit:jgit-archive', '//lib/joda:joda-time', '//lib/log:api', + '//lib/log:jsonevent-layout', '//lib/log:log4j', '//lib/lucene:analyzers-common', '//lib/lucene:core', @@ -66,7 +68,7 @@ '//lib/ow2:ow2-asm', '//lib/ow2:ow2-asm-tree', '//lib/ow2:ow2-asm-util', - '//lib/prolog:prolog-cafe', + '//lib/prolog:runtime', ], provided_deps = [ '//lib:servlet-api-3_1', @@ -94,16 +96,19 @@ ':server', '//gerrit-common:server', '//gerrit-cache-h2:cache-h2', + '//gerrit-extension-api:api', '//gerrit-lucene:lucene', '//gerrit-reviewdb:server', '//lib:guava', '//lib:gwtorm', '//lib:h2', '//lib:junit', + '//lib/auto:auto-value', '//lib/guice:guice', '//lib/guice:guice-servlet', '//lib/jgit:jgit', '//lib/jgit:junit', + '//lib/log:api', '//lib/log:impl_log4j', '//lib/log:log4j', ], @@ -134,7 +139,7 @@ '//gerrit-common:server', '//lib:junit', '//lib/guice:guice', - '//lib/prolog:prolog-cafe', + '//lib/prolog:runtime', ], ) @@ -155,7 +160,7 @@ '//lib:truth', '//lib/jgit:jgit', '//lib/guice:guice', - '//lib/prolog:prolog-cafe', + '//lib/prolog:runtime', ], ) @@ -217,7 +222,7 @@ '//lib/joda:joda-time', '//lib:parboiled-core', '//lib:parboiled-java', - '//lib/prolog:prolog-cafe', + '//lib/prolog:runtime', ], source_under_test = [':server'], visibility = ['//tools/eclipse:classpath'],
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java index e25b7cb..14f12b8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java +++ b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
@@ -79,9 +79,15 @@ @Override public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } AuditEvent other = (AuditEvent) obj; return this.uuid.equals(other.uuid);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java new file mode 100644 index 0000000..78fe38c --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/audit/ExtendedHttpAuditEvent.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.audit; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Multimap; +import com.google.gerrit.extensions.restapi.RestResource; +import com.google.gerrit.extensions.restapi.RestView; +import com.google.gerrit.server.CurrentUser; + +import javax.servlet.http.HttpServletRequest; + +/** + * Extended audit event. Adds request, resource and view data to HttpAuditEvent. + */ +public class ExtendedHttpAuditEvent extends HttpAuditEvent { + public final HttpServletRequest httpRequest; + public final RestResource resource; + public final RestView<? extends RestResource> view; + + /** + * Creates a new audit event with results + * + * @param sessionId session id the event belongs to + * @param who principal that has generated the event + * @param httpRequest the HttpServletRequest + * @param when time-stamp of when the event started + * @param params parameters of the event + * @param result result of the event + * @param resource REST resource data + * @param view view rendering object + */ + public ExtendedHttpAuditEvent(String sessionId, CurrentUser who, + HttpServletRequest httpRequest, long when, Multimap<String, ?> params, + Object input, int status, Object result, RestResource resource, + RestView<RestResource> view) { + super(sessionId, who, httpRequest.getRequestURI(), when, params, httpRequest.getMethod(), + input, status, result); + this.httpRequest = Preconditions.checkNotNull(httpRequest); + this.resource = resource; + this.view = view; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java index 8bd082d..19c3145 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -14,13 +14,13 @@ package com.google.gerrit.common; -import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gerrit.common.data.ContributorAgreement; import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.reviewdb.client.Account; @@ -41,11 +41,11 @@ import com.google.gerrit.server.events.ChangeRestoredEvent; import com.google.gerrit.server.events.CommentAddedEvent; import com.google.gerrit.server.events.DraftPublishedEvent; -import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.EventFactory; import com.google.gerrit.server.events.HashtagsChangedEvent; import com.google.gerrit.server.events.MergeFailedEvent; import com.google.gerrit.server.events.PatchSetCreatedEvent; +import com.google.gerrit.server.events.ProjectCreatedEvent; import com.google.gerrit.server.events.RefUpdatedEvent; import com.google.gerrit.server.events.ReviewerAddedEvent; import com.google.gerrit.server.events.TopicChangedEvent; @@ -66,12 +66,14 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -88,7 +90,7 @@ /** Spawns local executables when a hook action occurs. */ @Singleton public class ChangeHookRunner implements ChangeHooks, EventDispatcher, - EventSource, LifecycleListener { + EventSource, LifecycleListener, NewProjectCreatedListener { /** A logger for this class. */ private static final Logger log = LoggerFactory.getLogger(ChangeHookRunner.class); @@ -99,18 +101,19 @@ bind(ChangeHooks.class).to(ChangeHookRunner.class); bind(EventDispatcher.class).to(ChangeHookRunner.class); bind(EventSource.class).to(ChangeHookRunner.class); + DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(ChangeHookRunner.class); listener().to(ChangeHookRunner.class); } } private static class EventListenerHolder { - final EventListener listener; - final CurrentUser user; + final EventListener listener; + final CurrentUser user; - EventListenerHolder(EventListener l, CurrentUser u) { - listener = l; - user = u; - } + EventListenerHolder(EventListener l, CurrentUser u) { + listener = l; + user = u; + } } /** Container class used to hold the return code and output of script hook execution */ @@ -169,44 +172,47 @@ /** Listeners to receive all changes as they happen. */ private final DynamicSet<EventListener> unrestrictedListeners; - /** Filename of the new patchset hook. */ - private final File patchsetCreatedHook; + /** Path of the new patchset hook. */ + private final Path patchsetCreatedHook; - /** Filename of the draft published hook. */ - private final File draftPublishedHook; + /** Path of the draft published hook. */ + private final Path draftPublishedHook; - /** Filename of the new comments hook. */ - private final File commentAddedHook; + /** Path of the new comments hook. */ + private final Path commentAddedHook; - /** Filename of the change merged hook. */ - private final File changeMergedHook; + /** Path of the change merged hook. */ + private final Path changeMergedHook; - /** Filename of the merge failed hook. */ - private final File mergeFailedHook; + /** Path of the merge failed hook. */ + private final Path mergeFailedHook; - /** Filename of the change abandoned hook. */ - private final File changeAbandonedHook; + /** Path of the change abandoned hook. */ + private final Path changeAbandonedHook; - /** Filename of the change restored hook. */ - private final File changeRestoredHook; + /** Path of the change restored hook. */ + private final Path changeRestoredHook; - /** Filename of the ref updated hook. */ - private final File refUpdatedHook; + /** Path of the ref updated hook. */ + private final Path refUpdatedHook; - /** Filename of the reviewer added hook. */ - private final File reviewerAddedHook; + /** Path of the reviewer added hook. */ + private final Path reviewerAddedHook; - /** Filename of the topic changed hook. */ - private final File topicChangedHook; + /** Path of the topic changed hook. */ + private final Path topicChangedHook; - /** Filename of the cla signed hook. */ - private final File claSignedHook; + /** Path of the cla signed hook. */ + private final Path claSignedHook; - /** Filename of the update hook. */ - private final File refUpdateHook; + /** Path of the update hook. */ + private final Path refUpdateHook; - /** Filename of the hashtags changed hook */ - private final File hashtagsChangedHook; + /** Path of the hashtags changed hook */ + private final Path hashtagsChangedHook; + + /** Path of the project created hook. */ + private final Path projectCreatedHook; private final String anonymousCowardName; @@ -240,15 +246,15 @@ * @param projectCache the project cache instance for the server. */ @Inject - public ChangeHookRunner(final WorkQueue queue, - final GitRepositoryManager repoManager, - final @GerritServerConfig Config config, - final @AnonymousCowardName String anonymousCowardName, - final SitePaths sitePath, - final ProjectCache projectCache, - final AccountCache accountCache, - final EventFactory eventFactory, - final DynamicSet<EventListener> unrestrictedListeners) { + public ChangeHookRunner(WorkQueue queue, + GitRepositoryManager repoManager, + @GerritServerConfig Config config, + @AnonymousCowardName String anonymousCowardName, + SitePaths sitePath, + ProjectCache projectCache, + AccountCache accountCache, + EventFactory eventFactory, + DynamicSet<EventListener> unrestrictedListeners) { this.anonymousCowardName = anonymousCowardName; this.repoManager = repoManager; this.hookQueue = queue.createQueue(1, "hook"); @@ -258,21 +264,31 @@ this.sitePaths = sitePath; this.unrestrictedListeners = unrestrictedListeners; - final File hooksPath = sitePath.resolve(getValue(config, "hooks", "path", sitePath.hooks_dir.getAbsolutePath())); + Path hooksPath; + String hooksPathConfig = config.getString("hooks", null, "path"); + if (hooksPathConfig != null) { + hooksPath = Paths.get(hooksPathConfig); + } else { + hooksPath = sitePath.hooks_dir; + } - patchsetCreatedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "patchsetCreatedHook", "patchset-created")).getPath()); - draftPublishedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "draftPublishedHook", "draft-published")).getPath()); - commentAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "commentAddedHook", "comment-added")).getPath()); - changeMergedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeMergedHook", "change-merged")).getPath()); - mergeFailedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "mergeFailedHook", "merge-failed")).getPath()); - changeAbandonedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeAbandonedHook", "change-abandoned")).getPath()); - changeRestoredHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeRestoredHook", "change-restored")).getPath()); - refUpdatedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "refUpdatedHook", "ref-updated")).getPath()); - reviewerAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "reviewerAddedHook", "reviewer-added")).getPath()); - topicChangedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "topicChangedHook", "topic-changed")).getPath()); - claSignedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "claSignedHook", "cla-signed")).getPath()); - refUpdateHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "refUpdateHook", "ref-update")).getPath()); - hashtagsChangedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "hashtagsChangedHook", "hashtags-changed")).getPath()); + // When adding a new hook, make sure to check that the setting name + // canonicalizes correctly in hook() below. + patchsetCreatedHook = hook(config, hooksPath, "patchset-created"); + draftPublishedHook = hook(config, hooksPath, "draft-published"); + commentAddedHook = hook(config, hooksPath, "comment-added"); + changeMergedHook = hook(config, hooksPath, "change-merged"); + mergeFailedHook = hook(config, hooksPath, "merge-failed"); + changeAbandonedHook = hook(config, hooksPath, "change-abandoned"); + changeRestoredHook = hook(config, hooksPath, "change-restored"); + refUpdatedHook = hook(config, hooksPath, "ref-updated"); + reviewerAddedHook = hook(config, hooksPath, "reviewer-added"); + topicChangedHook = hook(config, hooksPath, "topic-changed"); + claSignedHook = hook(config, hooksPath, "cla-signed"); + refUpdateHook = hook(config, hooksPath, "ref-update"); + hashtagsChangedHook = hook(config, hooksPath, "hashtags-changed"); + projectCreatedHook = hook(config, hooksPath, "project-created"); + syncHookTimeout = config.getInt("hooks", "syncHookTimeout", 30); syncHookThreadPool = Executors.newCachedThreadPool( new ThreadFactoryBuilder() @@ -280,28 +296,20 @@ .build()); } + private static Path hook(Config config, Path path, String name) { + String setting = name.replace("-", "") + "hook"; + String value = config.getString("hooks", null, setting); + return path.resolve(value != null ? value : name); + } + @Override public void addEventListener(EventListener listener, CurrentUser user) { - listeners.put(listener, new EventListenerHolder(listener, user)); + listeners.put(listener, new EventListenerHolder(listener, user)); } @Override public void removeEventListener(EventListener listener) { - listeners.remove(listener); - } - - /** - * Helper Method for getting values from the config. - * - * @param config Config file to get value from. - * @param section Section to look in. - * @param setting Setting to get. - * @param fallback Fallback value. - * @return Setting value if found, else fallback. - */ - private String getValue(final Config config, final String section, final String setting, final String fallback) { - final String result = config.getString(section, null, setting); - return Strings.isNullOrEmpty(result) ? fallback : result; + listeners.remove(listener); } /** @@ -310,20 +318,20 @@ * @param name Project to get repo for, * @return Repository or null. */ - private Repository openRepository(final Project.NameKey name) { - try { - return repoManager.openRepository(name); - } catch (IOException err) { - log.warn("Cannot open repository " + name.get(), err); - return null; - } + private Repository openRepository(Project.NameKey name) { + try { + return repoManager.openRepository(name); + } catch (IOException err) { + log.warn("Cannot open repository " + name.get(), err); + return null; + } } private void addArg(List<String> args, String name, String value) { - if (value != null) { - args.add(name); - args.add(value); - } + if (value != null) { + args.add(name); + args.add(value); + } } /** @@ -331,10 +339,10 @@ * */ @Override - public HookResult doRefUpdateHook(final Project project, final String refname, - final Account uploader, final ObjectId oldId, final ObjectId newId) { + public HookResult doRefUpdateHook(Project project, String refname, + Account uploader, ObjectId oldId, ObjectId newId) { - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--project", project.getName()); addArg(args, "--refname", refname); addArg(args, "--uploader", getDisplayName(uploader)); @@ -344,6 +352,20 @@ return runSyncHook(project.getNameKey(), refUpdateHook, args); } + @Override + public void doProjectCreatedHook(Project.NameKey project, String headName) { + ProjectCreatedEvent event = new ProjectCreatedEvent(); + event.projectName = project.get(); + event.headName = headName; + fireEvent(project, event); + + List<String> args = new ArrayList<>(); + addArg(args, "--project", project.get()); + addArg(args, "--head", headName); + + runHook(project, projectCreatedHook, args); + } + /** * Fire the Patchset Created Hook. * @@ -352,219 +374,222 @@ * @throws OrmException */ @Override - public void doPatchsetCreatedHook(final Change change, final PatchSet patchSet, - final ReviewDb db) throws OrmException { - final PatchSetCreatedEvent event = new PatchSetCreatedEvent(); - final AccountState uploader = accountCache.get(patchSet.getUploader()); - final AccountState owner = accountCache.get(change.getOwner()); + public void doPatchsetCreatedHook(Change change, PatchSet patchSet, + ReviewDb db) throws OrmException { + PatchSetCreatedEvent event = new PatchSetCreatedEvent(); + AccountState uploader = accountCache.get(patchSet.getUploader()); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.uploader = eventFactory.asAccountAttribute(uploader.getAccount()); - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.uploader = eventFactory.asAccountAttribute(uploader.getAccount()); + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--is-draft", String.valueOf(patchSet.isDraft())); - addArg(args, "--kind", String.valueOf(event.patchSet.kind)); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--uploader", getDisplayName(uploader.getAccount())); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--patchset", event.patchSet.number); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--is-draft", String.valueOf(patchSet.isDraft())); + addArg(args, "--kind", String.valueOf(event.patchSet.kind)); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--uploader", getDisplayName(uploader.getAccount())); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--patchset", event.patchSet.number); - runHook(change.getProject(), patchsetCreatedHook, args); + runHook(change.getProject(), patchsetCreatedHook, args); } @Override - public void doDraftPublishedHook(final Change change, final PatchSet patchSet, - final ReviewDb db) throws OrmException { - final DraftPublishedEvent event = new DraftPublishedEvent(); - final AccountState uploader = accountCache.get(patchSet.getUploader()); - final AccountState owner = accountCache.get(change.getOwner()); + public void doDraftPublishedHook(Change change, PatchSet patchSet, + ReviewDb db) throws OrmException { + DraftPublishedEvent event = new DraftPublishedEvent(); + AccountState uploader = accountCache.get(patchSet.getUploader()); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.uploader = eventFactory.asAccountAttribute(uploader.getAccount()); - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.uploader = eventFactory.asAccountAttribute(uploader.getAccount()); + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--uploader", getDisplayName(uploader.getAccount())); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--patchset", event.patchSet.number); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--uploader", getDisplayName(uploader.getAccount())); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--patchset", event.patchSet.number); - runHook(change.getProject(), draftPublishedHook, args); + runHook(change.getProject(), draftPublishedHook, args); } @Override - public void doCommentAddedHook(final Change change, final Account account, - final PatchSet patchSet, final String comment, final Map<String, Short> approvals, - final ReviewDb db) throws OrmException { - final CommentAddedEvent event = new CommentAddedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + public void doCommentAddedHook(Change change, Account account, + PatchSet patchSet, String comment, Map<String, Short> approvals, + ReviewDb db) throws OrmException { + CommentAddedEvent event = new CommentAddedEvent(); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.author = eventFactory.asAccountAttribute(account); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.comment = comment; + event.change = eventFactory.asChangeAttribute(change); + event.author = eventFactory.asAccountAttribute(account); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.comment = comment; - LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes(); - if (approvals.size() > 0) { - event.approvals = new ApprovalAttribute[approvals.size()]; - int i = 0; - for (Map.Entry<String, Short> approval : approvals.entrySet()) { - event.approvals[i++] = getApprovalAttribute(labelTypes, approval); - } - } - - fireEvent(change, event, db); - - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--is-draft", patchSet.isDraft() ? "true" : "false"); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--author", getDisplayName(account)); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--comment", comment == null ? "" : comment); + LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes(); + if (approvals.size() > 0) { + event.approvals = new ApprovalAttribute[approvals.size()]; + int i = 0; for (Map.Entry<String, Short> approval : approvals.entrySet()) { - LabelType lt = labelTypes.byLabel(approval.getKey()); - if (lt != null) { - addArg(args, "--" + lt.getName(), Short.toString(approval.getValue())); - } + event.approvals[i++] = getApprovalAttribute(labelTypes, approval); } + } - runHook(change.getProject(), commentAddedHook, args); + fireEvent(change, event, db); + + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--is-draft", patchSet.isDraft() ? "true" : "false"); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--author", getDisplayName(account)); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--comment", comment == null ? "" : comment); + for (Map.Entry<String, Short> approval : approvals.entrySet()) { + LabelType lt = labelTypes.byLabel(approval.getKey()); + if (lt != null) { + addArg(args, "--" + lt.getName(), Short.toString(approval.getValue())); + } + } + + runHook(change.getProject(), commentAddedHook, args); } @Override - public void doChangeMergedHook(final Change change, final Account account, - final PatchSet patchSet, final ReviewDb db, String mergeResultRev) - throws OrmException { - final ChangeMergedEvent event = new ChangeMergedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + public void doChangeMergedHook(Change change, Account account, + PatchSet patchSet, ReviewDb db, String mergeResultRev) + throws OrmException { + ChangeMergedEvent event = new ChangeMergedEvent(); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.submitter = eventFactory.asAccountAttribute(account); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.newRev = mergeResultRev; - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.submitter = eventFactory.asAccountAttribute(account); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.newRev = mergeResultRev; + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--submitter", getDisplayName(account)); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--newrev", mergeResultRev); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--submitter", getDisplayName(account)); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--newrev", mergeResultRev); - runHook(change.getProject(), changeMergedHook, args); + runHook(change.getProject(), changeMergedHook, args); } @Override - public void doMergeFailedHook(final Change change, final Account account, - final PatchSet patchSet, final String reason, - final ReviewDb db) throws OrmException { - final MergeFailedEvent event = new MergeFailedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + public void doMergeFailedHook(Change change, Account account, + PatchSet patchSet, String reason, + ReviewDb db) throws OrmException { + MergeFailedEvent event = new MergeFailedEvent(); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.submitter = eventFactory.asAccountAttribute(account); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.reason = reason; - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.submitter = eventFactory.asAccountAttribute(account); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.reason = reason; + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--submitter", getDisplayName(account)); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--reason", reason == null ? "" : reason); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--submitter", getDisplayName(account)); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--reason", reason == null ? "" : reason); - runHook(change.getProject(), mergeFailedHook, args); + runHook(change.getProject(), mergeFailedHook, args); } @Override - public void doChangeAbandonedHook(final Change change, final Account account, - final PatchSet patchSet, final String reason, final ReviewDb db) + public void doChangeAbandonedHook(Change change, Account account, + PatchSet patchSet, String reason, ReviewDb db) throws OrmException { - final ChangeAbandonedEvent event = new ChangeAbandonedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + ChangeAbandonedEvent event = new ChangeAbandonedEvent(); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.abandoner = eventFactory.asAccountAttribute(account); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.reason = reason; - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.abandoner = eventFactory.asAccountAttribute(account); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.reason = reason; + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--abandoner", getDisplayName(account)); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--reason", reason == null ? "" : reason); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--abandoner", getDisplayName(account)); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--reason", reason == null ? "" : reason); - runHook(change.getProject(), changeAbandonedHook, args); + runHook(change.getProject(), changeAbandonedHook, args); } @Override - public void doChangeRestoredHook(final Change change, final Account account, - final PatchSet patchSet, final String reason, final ReviewDb db) + public void doChangeRestoredHook(Change change, Account account, + PatchSet patchSet, String reason, ReviewDb db) throws OrmException { - final ChangeRestoredEvent event = new ChangeRestoredEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + ChangeRestoredEvent event = new ChangeRestoredEvent(); + AccountState owner = accountCache.get(change.getOwner()); - event.change = eventFactory.asChangeAttribute(change); - event.restorer = eventFactory.asAccountAttribute(account); - event.patchSet = eventFactory.asPatchSetAttribute(patchSet); - event.reason = reason; - fireEvent(change, event, db); + event.change = eventFactory.asChangeAttribute(change); + event.restorer = eventFactory.asAccountAttribute(account); + event.patchSet = eventFactory.asPatchSetAttribute(patchSet); + event.reason = reason; + fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); - addArg(args, "--change", event.change.id); - addArg(args, "--change-url", event.change.url); - addArg(args, "--change-owner", getDisplayName(owner.getAccount())); - addArg(args, "--project", event.change.project); - addArg(args, "--branch", event.change.branch); - addArg(args, "--topic", event.change.topic); - addArg(args, "--restorer", getDisplayName(account)); - addArg(args, "--commit", event.patchSet.revision); - addArg(args, "--reason", reason == null ? "" : reason); + List<String> args = new ArrayList<>(); + addArg(args, "--change", event.change.id); + addArg(args, "--change-url", event.change.url); + addArg(args, "--change-owner", getDisplayName(owner.getAccount())); + addArg(args, "--project", event.change.project); + addArg(args, "--branch", event.change.branch); + addArg(args, "--topic", event.change.topic); + addArg(args, "--restorer", getDisplayName(account)); + addArg(args, "--commit", event.patchSet.revision); + addArg(args, "--reason", reason == null ? "" : reason); - runHook(change.getProject(), changeRestoredHook, args); + runHook(change.getProject(), changeRestoredHook, args); } @Override - public void doRefUpdatedHook(final Branch.NameKey refName, final RefUpdate refUpdate, final Account account) { - doRefUpdatedHook(refName, refUpdate.getOldObjectId(), refUpdate.getNewObjectId(), account); + public void doRefUpdatedHook(Branch.NameKey refName, RefUpdate refUpdate, + Account account) { + doRefUpdatedHook(refName, refUpdate.getOldObjectId(), + refUpdate.getNewObjectId(), account); } @Override - public void doRefUpdatedHook(final Branch.NameKey refName, final ObjectId oldId, final ObjectId newId, final Account account) { - final RefUpdatedEvent event = new RefUpdatedEvent(); + public void doRefUpdatedHook(Branch.NameKey refName, ObjectId oldId, + ObjectId newId, Account account) { + RefUpdatedEvent event = new RefUpdatedEvent(); if (account != null) { event.submitter = eventFactory.asAccountAttribute(account); @@ -572,7 +597,7 @@ event.refUpdate = eventFactory.asRefUpdateAttribute(oldId, newId, refName); fireEvent(refName, event); - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--oldrev", event.refUpdate.oldRev); addArg(args, "--newrev", event.refUpdate.newRev); addArg(args, "--refname", event.refUpdate.refName); @@ -585,17 +610,17 @@ } @Override - public void doReviewerAddedHook(final Change change, final Account account, - final PatchSet patchSet, final ReviewDb db) throws OrmException { - final ReviewerAddedEvent event = new ReviewerAddedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + public void doReviewerAddedHook(Change change, Account account, + PatchSet patchSet, ReviewDb db) throws OrmException { + ReviewerAddedEvent event = new ReviewerAddedEvent(); + AccountState owner = accountCache.get(change.getOwner()); event.change = eventFactory.asChangeAttribute(change); event.patchSet = eventFactory.asPatchSetAttribute(patchSet); event.reviewer = eventFactory.asAccountAttribute(account); fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--change", event.change.id); addArg(args, "--change-url", event.change.url); addArg(args, "--change-owner", getDisplayName(owner.getAccount())); @@ -607,18 +632,18 @@ } @Override - public void doTopicChangedHook(final Change change, final Account account, - final String oldTopic, final ReviewDb db) + public void doTopicChangedHook(Change change, Account account, + String oldTopic, ReviewDb db) throws OrmException { - final TopicChangedEvent event = new TopicChangedEvent(); - final AccountState owner = accountCache.get(change.getOwner()); + TopicChangedEvent event = new TopicChangedEvent(); + AccountState owner = accountCache.get(change.getOwner()); event.change = eventFactory.asChangeAttribute(change); event.changer = eventFactory.asAccountAttribute(account); event.oldTopic = oldTopic; fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--change", event.change.id); addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--project", event.change.project); @@ -653,7 +678,7 @@ fireEvent(change, event, db); - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--change", event.change.id); addArg(args, "--change-owner", getDisplayName(owner.getAccount())); addArg(args, "--project", event.change.project); @@ -680,7 +705,7 @@ @Override public void doClaSignupHook(Account account, ContributorAgreement cla) { if (account != null) { - final List<String> args = new ArrayList<>(); + List<String> args = new ArrayList<>(); addArg(args, "--submitter", getDisplayName(account)); addArg(args, "--user-id", account.getId().toString()); addArg(args, "--cla-name", cla.getName()); @@ -690,60 +715,85 @@ } @Override - public void postEvent(final Change change, final Event event, - final ReviewDb db) throws OrmException { + public void postEvent(Change change, com.google.gerrit.server.events.Event event, + ReviewDb db) throws OrmException { fireEvent(change, event, db); } @Override - public void postEvent(final Branch.NameKey branchName, - final Event event) { + public void postEvent(Branch.NameKey branchName, com.google.gerrit.server.events.Event event) { fireEvent(branchName, event); } - private void fireEventForUnrestrictedListeners(final Event event) { + private void fireEventForUnrestrictedListeners(com.google.gerrit.server.events.Event event) { for (EventListener listener : unrestrictedListeners) { - listener.onEvent(event); + listener.onEvent(event); } } - private void fireEvent(final Change change, final Event event, - final ReviewDb db) throws OrmException { + private void fireEvent(Change change, com.google.gerrit.server.events.Event event, + ReviewDb db) throws OrmException { for (EventListenerHolder holder : listeners.values()) { - if (isVisibleTo(change, holder.user, db)) { - holder.listener.onEvent(event); - } - } - - fireEventForUnrestrictedListeners( event ); - } - - private void fireEvent(Branch.NameKey branchName, final Event event) { - for (EventListenerHolder holder : listeners.values()) { - if (isVisibleTo(branchName, holder.user)) { - holder.listener.onEvent(event); - } - } - - fireEventForUnrestrictedListeners( event ); - } - - private boolean isVisibleTo(Change change, CurrentUser user, ReviewDb db) throws OrmException { - final ProjectState pe = projectCache.get(change.getProject()); - if (pe == null) { - return false; + if (isVisibleTo(change, holder.user, db)) { + holder.listener.onEvent(event); } - final ProjectControl pc = pe.controlFor(user); - return pc.controlFor(change).isVisible(db); + } + + fireEventForUnrestrictedListeners( event ); + } + + private void fireEvent(Project.NameKey project, ProjectCreatedEvent event) { + for (EventListenerHolder holder : listeners.values()) { + if (isVisibleTo(project, event, holder.user)) { + holder.listener.onEvent(event); + } + } + + fireEventForUnrestrictedListeners(event); + } + + private void fireEventForUnrestrictedListeners(ProjectCreatedEvent event) { + for (EventListener listener : unrestrictedListeners) { + listener.onEvent(event); + } + } + + private boolean isVisibleTo(Project.NameKey project, ProjectCreatedEvent event, CurrentUser user) { + ProjectState pe = projectCache.get(project); + if (pe == null) { + return false; + } + ProjectControl pc = pe.controlFor(user); + return pc.controlForRef(event.getHeadName()).isVisible(); + } + + private void fireEvent(Branch.NameKey branchName, com.google.gerrit.server.events.Event event) { + for (EventListenerHolder holder : listeners.values()) { + if (isVisibleTo(branchName, holder.user)) { + holder.listener.onEvent(event); + } + } + + fireEventForUnrestrictedListeners(event); + } + + private boolean isVisibleTo(Change change, CurrentUser user, ReviewDb db) + throws OrmException { + ProjectState pe = projectCache.get(change.getProject()); + if (pe == null) { + return false; + } + ProjectControl pc = pe.controlFor(user); + return pc.controlFor(change).isVisible(db); } private boolean isVisibleTo(Branch.NameKey branchName, CurrentUser user) { - final ProjectState pe = projectCache.get(branchName.getParentKey()); - if (pe == null) { - return false; - } - final ProjectControl pc = pe.controlFor(user); - return pc.controlForRef(branchName).isVisible(); + ProjectState pe = projectCache.get(branchName.getParentKey()); + if (pe == null) { + return false; + } + ProjectControl pc = pe.controlFor(user); + return pc.controlForRef(branchName).isVisible(); } /** @@ -753,14 +803,14 @@ */ private ApprovalAttribute getApprovalAttribute(LabelTypes labelTypes, Entry<String, Short> approval) { - ApprovalAttribute a = new ApprovalAttribute(); - a.type = approval.getKey(); - LabelType lt = labelTypes.byLabel(approval.getKey()); - if (lt != null) { - a.description = lt.getName(); - } - a.value = Short.toString(approval.getValue()); - return a; + ApprovalAttribute a = new ApprovalAttribute(); + a.type = approval.getKey(); + LabelType lt = labelTypes.byLabel(approval.getKey()); + if (lt != null) { + a.description = lt.getName(); + } + a.value = Short.toString(approval.getValue()); + return a; } /** @@ -769,16 +819,18 @@ * @param account Account to get name for. * @return Name for this account. */ - private String getDisplayName(final Account account) { - if (account != null) { - String result = (account.getFullName() == null) ? anonymousCowardName : account.getFullName(); - if (account.getPreferredEmail() != null) { - result += " (" + account.getPreferredEmail() + ")"; - } - return result; + private String getDisplayName(Account account) { + if (account != null) { + String result = (account.getFullName() == null) + ? anonymousCowardName + : account.getFullName(); + if (account.getPreferredEmail() != null) { + result += " (" + account.getPreferredEmail() + ")"; } + return result; + } - return anonymousCowardName; + return anonymousCowardName; } /** @@ -788,23 +840,23 @@ * @param hook the hook to execute. * @param args Arguments to use to run the hook. */ - private synchronized void runHook(Project.NameKey project, File hook, + private synchronized void runHook(Project.NameKey project, Path hook, List<String> args) { - if (project != null && hook.exists()) { + if (project != null && Files.exists(hook)) { hookQueue.execute(new AsyncHookTask(project, hook, args)); } } - private synchronized void runHook(File hook, List<String> args) { - if (hook.exists()) { + private synchronized void runHook(Path hook, List<String> args) { + if (Files.exists(hook)) { hookQueue.execute(new AsyncHookTask(null, hook, args)); } } private HookResult runSyncHook(Project.NameKey project, - File hook, List<String> args) { + Path hook, List<String> args) { - if (!hook.exists()) { + if (!Files.exists(hook)) { return null; } @@ -818,10 +870,10 @@ try { return task.get(syncHookTimeout, TimeUnit.SECONDS); } catch (TimeoutException e) { - message = "Synchronous hook timed out " + hook.getAbsolutePath(); + message = "Synchronous hook timed out " + hook.toAbsolutePath(); log.error(message); } catch (Exception e) { - message = "Error running hook " + hook.getAbsolutePath(); + message = "Error running hook " + hook.toAbsolutePath(); log.error(message, e); } @@ -849,12 +901,12 @@ private class HookTask { private final Project.NameKey project; - private final File hook; + private final Path hook; private final List<String> args; private StringWriter output; private Process ps; - protected HookTask(Project.NameKey project, File hook, List<String> args) { + protected HookTask(Project.NameKey project, Path hook, List<String> args) { this.project = project; this.hook = hook; this.args = args; @@ -869,19 +921,19 @@ HookResult result = null; try { - final List<String> argv = new ArrayList<>(1 + args.size()); - argv.add(hook.getAbsolutePath()); + List<String> argv = new ArrayList<>(1 + args.size()); + argv.add(hook.toAbsolutePath().toString()); argv.addAll(args); - final ProcessBuilder pb = new ProcessBuilder(argv); + ProcessBuilder pb = new ProcessBuilder(argv); pb.redirectErrorStream(true); if (project != null) { repo = openRepository(project); } - final Map<String, String> env = pb.environment(); - env.put("GERRIT_SITE", sitePaths.site_path.getAbsolutePath()); + Map<String, String> env = pb.environment(); + env.put("GERRIT_SITE", sitePaths.site_path.toAbsolutePath().toString()); if (repo != null) { pb.directory(repo.getDirectory()); @@ -906,7 +958,7 @@ } catch (InterruptedException iex) { // InterruptedExeception - timeout or cancel } catch (Throwable err) { - log.error("Error running hook " + hook.getAbsolutePath(), err); + log.error("Error running hook " + hook.toAbsolutePath(), err); } finally { if (repo != null) { repo.close(); @@ -914,7 +966,7 @@ } if (result != null) { - final int exitValue = result.getExitValue(); + int exitValue = result.getExitValue(); if (exitValue == 0) { log.debug("hook[" + getName() + "] exitValue:" + exitValue); } else { @@ -949,12 +1001,12 @@ } protected String getName() { - return hook.getName(); + return hook.getFileName().toString(); } @Override public String toString() { - return "hook " + hook.getName(); + return "hook " + hook.getFileName(); } public void cancel() { @@ -966,7 +1018,7 @@ private final class SyncHookTask extends HookTask implements Callable<HookResult> { - private SyncHookTask(Project.NameKey project, File hook, List<String> args) { + private SyncHookTask(Project.NameKey project, Path hook, List<String> args) { super(project, hook, args); } @@ -979,7 +1031,7 @@ /** Runnable type used to run asynchronous hooks */ private final class AsyncHookTask extends HookTask implements Runnable { - private AsyncHookTask(Project.NameKey project, File hook, List<String> args) { + private AsyncHookTask(Project.NameKey project, Path hook, List<String> args) { super(project, hook, args); } @@ -988,4 +1040,10 @@ super.runHook(); } } + + @Override + public void onNewProjectCreated(NewProjectCreatedListener.Event event) { + Project.NameKey project = new Project.NameKey(event.getProjectName()); + doProjectCreatedHook(project, event.getHeadName()); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java index 7f7e8b2..b16a8a5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java +++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
@@ -181,4 +181,12 @@ public void doHashtagsChangedHook(Change change, Account account, Set<String>added, Set<String> removed, Set<String> hashtags, ReviewDb db) throws OrmException; + + /** + * Fire the project created hook + * + * @param project The project that was created + * @param headName The head name of the created project + */ + public void doProjectCreatedHook(Project.NameKey project, String headName); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java index 156672e..bed77a7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java +++ b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
@@ -114,6 +114,10 @@ } @Override + public void doProjectCreatedHook(Project.NameKey project, String headName) { + } + + @Override public void postEvent(Change change, Event event, ReviewDb db) { }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java index 2afdffc..26c0fd0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologEnvironment.java
@@ -28,6 +28,7 @@ import com.googlecode.prolog_cafe.lang.BufferingPrologControl; import com.googlecode.prolog_cafe.lang.Predicate; +import com.googlecode.prolog_cafe.lang.PredicateEncoder; import com.googlecode.prolog_cafe.lang.Prolog; import com.googlecode.prolog_cafe.lang.PrologMachineCopy; @@ -50,11 +51,8 @@ * A single copy of the Prolog interpreter, for the current thread. */ public class PrologEnvironment extends BufferingPrologControl { - private static final Logger log = - LoggerFactory.getLogger(PrologEnvironment.class); - - static final int MAX_ARITY = 8; + LoggerFactory.getLogger(PrologEnvironment.class); public static interface Factory { /** @@ -68,19 +66,14 @@ private final Args args; private final Map<StoredValue<Object>, Object> storedValues; - private int reductionLimit; - private int reductionsRemaining; private List<Runnable> cleanup; @Inject PrologEnvironment(Args a, @Assisted PrologMachineCopy src) { super(src); - setMaxArity(MAX_ARITY); setEnabled(EnumSet.allOf(Prolog.Feature.class), false); args = a; storedValues = new HashMap<>(); - reductionLimit = a.reductionLimit; - reductionsRemaining = reductionLimit; cleanup = new LinkedList<>(); } @@ -89,25 +82,9 @@ } @Override - public boolean isEngineStopped() { - if (super.isEngineStopped()) { - return true; - } else if (--reductionsRemaining <= 0) { - throw new ReductionLimitException(reductionLimit); - } - return false; - } - - @Override public void setPredicate(Predicate goal) { super.setPredicate(goal); - reductionLimit = args.reductionLimit(goal); - reductionsRemaining = reductionLimit; - } - - /** @return number of reductions during execution. */ - public int getReductions() { - return reductionLimit - reductionsRemaining; + setReductionLimit(args.reductionLimit(goal)); } /** @@ -177,6 +154,19 @@ @Singleton public static class Args { + private static final Class<Predicate> CONSULT_STREAM_2; + static { + try { + @SuppressWarnings("unchecked") + Class<Predicate> c = (Class<Predicate>) Class.forName( + PredicateEncoder.encode(Prolog.BUILTIN, "consult_stream", 2), + false, RulesCache.class.getClassLoader()); + CONSULT_STREAM_2 = c; + } catch (ClassNotFoundException e) { + throw new LinkageError("cannot find predicate consult_stream", e); + } + } + private final ProjectCache projectCache; private final GitRepositoryManager repositoryManager; private final PatchListCache patchListCache; @@ -210,8 +200,7 @@ } private int reductionLimit(Predicate goal) { - if ("com.googlecode.prolog_cafe.builtin.PRED_consult_stream_2" - .equals(goal.getClass().getName())) { + if (goal.getClass() == CONSULT_STREAM_2) { return compileLimit; } return reductionLimit;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/ReductionLimitException.java b/gerrit-server/src/main/java/com/google/gerrit/rules/ReductionLimitException.java deleted file mode 100644 index 2c27240..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/ReductionLimitException.java +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright (C) 2014 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.rules; - -/** Thrown by {@link PrologEnvironment} if a script runs too long. */ -public class ReductionLimitException extends RuntimeException { - private static final long serialVersionUID = 1L; - - ReductionLimitException(int limit) { - super(String.format("exceeded reduction limit of %d", limit)); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java index 5dea6a2..658bb35 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java
@@ -16,6 +16,7 @@ import static com.googlecode.prolog_cafe.lang.PrologMachineCopy.save; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.gerrit.extensions.registration.DynamicSet; @@ -27,13 +28,18 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; +import com.googlecode.prolog_cafe.exceptions.SyntaxException; +import com.googlecode.prolog_cafe.exceptions.TermException; import com.googlecode.prolog_cafe.lang.BufferingPrologControl; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; +import com.googlecode.prolog_cafe.lang.ListTerm; import com.googlecode.prolog_cafe.lang.Prolog; import com.googlecode.prolog_cafe.lang.PrologClassLoader; import com.googlecode.prolog_cafe.lang.PrologMachineCopy; +import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; +import com.googlecode.prolog_cafe.lang.Term; import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.errors.RepositoryNotFoundException; @@ -44,10 +50,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.RawParseUtils; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.PushbackReader; import java.io.Reader; import java.io.StringReader; @@ -57,6 +60,8 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -96,8 +101,8 @@ } private final boolean enableProjectRules; - private final File cacheDir; - private final File rulesDir; + private final Path cacheDir; + private final Path rulesDir; private final GitRepositoryManager gitMgr; private final DynamicSet<PredicateProvider> predicateProviders; private final ClassLoader systemLoader; @@ -108,7 +113,7 @@ GitRepositoryManager gm, DynamicSet<PredicateProvider> predicateProviders) { enableProjectRules = config.getBoolean("rules", null, "enable", true); cacheDir = site.resolve(config.getString("cache", null, "directory")); - rulesDir = cacheDir != null ? new File(cacheDir, "rules") : null; + rulesDir = cacheDir != null ? cacheDir.resolve("rules") : null; gitMgr = gm; this.predicateProviders = predicateProviders; @@ -153,9 +158,9 @@ return pcm; } - public PrologMachineCopy loadMachine(String name, InputStream in) + public PrologMachineCopy loadMachine(String name, Reader in) throws CompileException { - PrologMachineCopy pmc = consultRules(name, new InputStreamReader(in)); + PrologMachineCopy pmc = consultRules(name, in); if (pmc == null) { throw new CompileException("Cannot consult rules from the stream " + name); } @@ -178,9 +183,9 @@ // that over dynamic consult as the bytecode will be faster. // if (rulesDir != null) { - File jarFile = new File(rulesDir, "rules-" + rulesId.getName() + ".jar"); - if (jarFile.isFile()) { - URL[] cp = new URL[] {toURL(jarFile)}; + Path jarPath = rulesDir.resolve("rules-" + rulesId.getName() + ".jar"); + if (Files.isRegularFile(jarPath)) { + URL[] cp = new URL[] {toURL(jarPath)}; return save(newEmptyMachine(new URLClassLoader(cp, systemLoader))); } } @@ -204,12 +209,60 @@ SymbolTerm.intern(name), new JavaObjectTerm(in))) { return null; } + } catch (SyntaxException e) { + throw new CompileException(e.toString(), e); + } catch (TermException e) { + Term m = e.getMessageTerm(); + if (m instanceof StructureTerm && "syntax_error".equals(m.name()) + && m.arity() >= 1) { + StringBuilder msg = new StringBuilder(); + if (m.arg(0) instanceof ListTerm) { + msg.append(Joiner.on(' ').join(((ListTerm) m.arg(0)).toJava())); + } else { + msg.append(m.arg(0).toString()); + } + if (m.arity() == 2 && m.arg(1) instanceof StructureTerm + && "at".equals(m.arg(1).name())) { + Term at = m.arg(1).arg(0).dereference(); + if (at instanceof ListTerm) { + msg.append(" at: "); + msg.append(prettyProlog(at)); + } + } + throw new CompileException(msg.toString(), e); + } + throw new CompileException("Error while consulting rules from " + name, e); } catch (RuntimeException e) { throw new CompileException("Error while consulting rules from " + name, e); } return save(ctl); } + private static String prettyProlog(Term at) { + StringBuilder b = new StringBuilder(); + for (Object o : ((ListTerm) at).toJava()) { + if (o instanceof Term) { + Term t = (Term) o; + if (!(t instanceof StructureTerm)) { + b.append(t.toString()).append(' '); + continue; + } + switch (t.name()) { + case "atom": + SymbolTerm atom = (SymbolTerm) t.arg(0); + b.append(atom.toString()); + break; + case "var": + b.append(t.arg(0).toString()); + break; + } + } else { + b.append(o); + } + } + return b.toString().trim(); + } + private String read(Project.NameKey project, ObjectId rulesId) throws CompileException { Repository git; @@ -237,7 +290,6 @@ private BufferingPrologControl newEmptyMachine(ClassLoader cl) { BufferingPrologControl ctl = new BufferingPrologControl(); - ctl.setMaxArity(PrologEnvironment.MAX_ARITY); ctl.setMaxDatabaseSize(DB_MAX); ctl.setPrologClassLoader(new PrologClassLoader(new PredicateClassLoader( predicateProviders, cl))); @@ -254,11 +306,11 @@ return ctl; } - private static URL toURL(File jarFile) throws CompileException { + private static URL toURL(Path jarPath) throws CompileException { try { - return jarFile.toURI().toURL(); + return jarPath.toUri().toURL(); } catch (MalformedURLException e) { - throw new CompileException("Cannot create URL for " + jarFile, e); + throw new CompileException("Cannot create URL for " + jarPath, e); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValue.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValue.java index d454e40..27f15e6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValue.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValue.java
@@ -14,8 +14,8 @@ package com.google.gerrit.rules; +import com.googlecode.prolog_cafe.exceptions.SystemException; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.SystemException; /** * Defines a value cached in a {@link PrologEnvironment}.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java index 6136742..c9359b4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -38,8 +38,8 @@ import com.google.gerrit.server.query.change.ChangeData; import com.google.gwtorm.server.OrmException; +import com.googlecode.prolog_cafe.exceptions.SystemException; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.SystemException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java index bd175a9..c31a411 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -142,7 +142,7 @@ private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) throws OrmException { - Collection<PatchSet> patchSets = cd.patches(); + Collection<PatchSet> patchSets = cd.patchSets(); TreeMap<Integer, PatchSet> result = Maps.newTreeMap(); for (PatchSet ps : patchSets) { result.put(ps.getId().get(), ps);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java index 9faa516..305b66d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -17,9 +17,11 @@ import static com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy.RECEIVE_COMMITS; import static com.google.gerrit.server.query.change.ChangeData.asChanges; +import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.reviewdb.client.Change; @@ -95,6 +97,17 @@ private static final Logger log = LoggerFactory.getLogger(ChangeUtil.class); + public static final Function<PatchSet, Integer> TO_PS_ID = + new Function<PatchSet, Integer>() { + @Override + public Integer apply(PatchSet in) { + return in.getId().get(); + } + }; + + public static final Ordering<PatchSet> PS_ID_ORDER = Ordering.natural() + .onResultOf(TO_PS_ID); + /** * Generate a new unique identifier for change message entities. * @@ -104,7 +117,8 @@ * @throws OrmException the database couldn't be incremented. */ public static String messageUUID(ReviewDb db) throws OrmException { - int p, s; + int p; + int s; synchronized (uuidLock) { if (uuidSeq == 0) { uuidPrefix = db.nextChangeMessageId(); @@ -316,7 +330,7 @@ ins.setMessage(cmsg).insert(); try { - RevertedSender cm = revertedSenderFactory.create(change); + RevertedSender cm = revertedSenderFactory.create(change.getId()); cm.setFrom(user().getAccountId()); cm.setChangeMessage(cmsg); cm.send(); @@ -456,7 +470,7 @@ throw new IOException("Failed to delete ref " + patch.getRefName() + " in " + repo.getDirectory() + ": " + update.getResult()); } - gitRefUpdated.fire(change.getProject(), update); + gitRefUpdated.fire(change.getProject(), update, ReceiveCommand.Type.DELETE); } finally { repo.close(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java index be5e000..baba4bb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -14,6 +14,8 @@ package com.google.gerrit.server; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.gerrit.common.Nullable; @@ -37,6 +39,7 @@ import com.google.gerrit.server.config.DisableReverseDnsLookup; import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gwtorm.server.OrmException; +import com.google.gwtorm.server.OrmRuntimeException; import com.google.gwtorm.server.ResultSet; import com.google.inject.Inject; import com.google.inject.OutOfScopeException; @@ -144,13 +147,13 @@ CapabilityControl.Factory capabilityControlFactory, final AuthConfig authConfig, Realm realm, - final @AnonymousCowardName String anonymousCowardName, - final @CanonicalWebUrl Provider<String> canonicalUrl, + @AnonymousCowardName final String anonymousCowardName, + @CanonicalWebUrl final Provider<String> canonicalUrl, final AccountCache accountCache, final GroupBackend groupBackend, - final @DisableReverseDnsLookup Boolean disableReverseDnsLookup, + @DisableReverseDnsLookup final Boolean disableReverseDnsLookup, - final @RemotePeer Provider<SocketAddress> remotePeerProvider, + @RemotePeer final Provider<SocketAddress> remotePeerProvider, final Provider<ReviewDb> dbProvider) { this.capabilityControlFactory = capabilityControlFactory; this.authConfig = authConfig; @@ -327,35 +330,28 @@ @Override public Set<Change.Id> getStarredChanges() { if (starredChanges == null) { - if (dbProvider == null) { - throw new OutOfScopeException("Not in request scoped user"); - } - Set<Change.Id> h = Sets.newHashSet(); + checkRequestScope(); try { - if (starredQuery != null) { - for (StarredChange sc : starredQuery) { - h.add(sc.getChangeId()); - } - starredQuery = null; - } else { - for (StarredChange sc : dbProvider.get().starredChanges() - .byAccount(getAccountId())) { - h.add(sc.getChangeId()); - } - } - } catch (OrmException e) { - log.warn("Cannot query starred by user changes", e); + starredChanges = starredChangeIds( + starredQuery != null ? starredQuery : starredQuery()); + } catch (OrmException | OrmRuntimeException e) { + log.warn("Cannot query starred changes", e); } - starredChanges = Collections.unmodifiableSet(h); } return starredChanges; } + public void clearStarredChanges() { + // Async query may have started before an update that the caller expects + // to see the results of, so we can't trust it. + abortStarredChanges(); + starredChanges = null; + } + public void asyncStarredChanges() { if (starredChanges == null && dbProvider != null) { try { - starredQuery = - dbProvider.get().starredChanges().byAccount(getAccountId()); + starredQuery = starredQuery(); } catch (OrmException e) { log.warn("Cannot query starred by user changes", e); starredQuery = null; @@ -374,12 +370,31 @@ } } + private void checkRequestScope() { + if (dbProvider == null) { + throw new OutOfScopeException("Not in request scoped user"); + } + } + + private ResultSet<StarredChange> starredQuery() throws OrmException { + return dbProvider.get().starredChanges().byAccount(getAccountId()); + } + + private static ImmutableSet<Change.Id> starredChangeIds( + Iterable<StarredChange> scs) { + return FluentIterable.from(scs) + .transform(new Function<StarredChange, Change.Id>() { + @Override + public Change.Id apply(StarredChange in) { + return in.getChangeId(); + } + }).toSet(); + } + @Override public Collection<AccountProjectWatch> getNotificationFilters() { if (notificationFilters == null) { - if (dbProvider == null) { - throw new OutOfScopeException("Not in request scoped user"); - } + checkRequestScope(); List<AccountProjectWatch> r; try { r = dbProvider.get().accountProjectWatches() //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java index d5242c2..88f034e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java
@@ -14,12 +14,18 @@ package com.google.gerrit.server; +import static com.google.common.base.MoreObjects.firstNonNull; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Predicate; +import com.google.common.collect.ComparisonChain; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.gerrit.extensions.client.Side; +import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchLineComment; @@ -62,6 +68,47 @@ */ @Singleton public class PatchLineCommentsUtil { + public static Ordering<PatchLineComment> PLC_ORDER = + new Ordering<PatchLineComment>() { + @Override + public int compare(PatchLineComment c1, PatchLineComment c2) { + String filename1 = c1.getKey().getParentKey().get(); + String filename2 = c2.getKey().getParentKey().get(); + return ComparisonChain.start() + .compare(filename1, filename2) + .compare(getCommentPsId(c1).get(), getCommentPsId(c2).get()) + .compare(c1.getSide(), c2.getSide()) + .compare(c1.getLine(), c2.getLine()) + .compare(c1.getWrittenOn(), c2.getWrittenOn()) + .result(); + } + }; + + public static final Ordering<CommentInfo> COMMENT_INFO_ORDER = + new Ordering<CommentInfo>() { + @Override + public int compare(CommentInfo a, CommentInfo b) { + return ComparisonChain.start() + .compare(a.path, b.path, NULLS_FIRST) + .compare(a.patchSet, b.patchSet, NULLS_FIRST) + .compare(side(a), side(b)) + .compare(a.line, b.line, NULLS_FIRST) + .compare(a.id, b.id) + .result(); + } + + private int side(CommentInfo c) { + return firstNonNull(c.side, Side.REVISION).ordinal(); + } + }; + + public static PatchSet.Id getCommentPsId(PatchLineComment plc) { + return plc.getKey().getParentKey().getParentKey(); + } + + private static final Ordering<Comparable<?>> NULLS_FIRST = + Ordering.natural().nullsFirst(); + private final GitRepositoryManager repoManager; private final AllUsersName allUsers; private final DraftCommentNotes.Factory draftFactory; @@ -106,8 +153,7 @@ notes.load(); List<PatchLineComment> comments = Lists.newArrayList(); - comments.addAll(notes.getBaseComments().values()); - comments.addAll(notes.getPatchSetComments().values()); + comments.addAll(notes.getComments().values()); return sort(comments); } @@ -166,13 +212,7 @@ return sort( db.patchComments().publishedByChangeFile(changeId, file).toList()); } - notes.load(); - List<PatchLineComment> comments = Lists.newArrayList(); - - addCommentsOnFile(comments, notes.getBaseComments().values(), file); - addCommentsOnFile(comments, notes.getPatchSetComments().values(), - file); - return sort(comments); + return commentsOnFile(notes.load().getComments().values(), file); } public List<PatchLineComment> publishedByPatchSet(ReviewDb db, @@ -181,11 +221,7 @@ return sort( db.patchComments().publishedByPatchSet(psId).toList()); } - notes.load(); - List<PatchLineComment> comments = new ArrayList<>(); - comments.addAll(notes.getPatchSetComments().get(psId)); - comments.addAll(notes.getBaseComments().get(psId)); - return sort(comments); + return commentsOnPatchSet(notes.load().getComments().values(), psId); } public List<PatchLineComment> draftByPatchSetAuthor(ReviewDb db, @@ -195,11 +231,8 @@ return sort( db.patchComments().draftByPatchSetAuthor(psId, author).toList()); } - - List<PatchLineComment> comments = Lists.newArrayList(); - comments.addAll(notes.getDraftBaseComments(author).row(psId).values()); - comments.addAll(notes.getDraftPsComments(author).row(psId).values()); - return sort(comments); + return commentsOnPatchSet( + notes.load().getDraftComments(author).values(), psId); } public List<PatchLineComment> draftByChangeFileAuthor(ReviewDb db, @@ -211,12 +244,8 @@ .draftByChangeFileAuthor(notes.getChangeId(), file, author) .toList()); } - List<PatchLineComment> comments = Lists.newArrayList(); - addCommentsOnFile(comments, notes.getDraftBaseComments(author).values(), - file); - addCommentsOnFile(comments, notes.getDraftPsComments(author).values(), - file); - return sort(comments); + return commentsOnFile( + notes.load().getDraftComments(author).values(), file); } public List<PatchLineComment> draftByChangeAuthor(ReviewDb db, @@ -233,11 +262,10 @@ in.getKey().getParentKey().getParentKey().getParentKey(); return changeId.equals(matchId); } - }).toSortedList(ChangeNotes.PLC_ORDER); + }).toSortedList(PLC_ORDER); } List<PatchLineComment> comments = Lists.newArrayList(); - comments.addAll(notes.getDraftBaseComments(author).values()); - comments.addAll(notes.getDraftPsComments(author).values()); + comments.addAll(notes.getDraftComments(author).values()); return sort(comments); } @@ -247,9 +275,8 @@ return sort(db.patchComments().draftByAuthor(author).toList()); } - Set<String> refNames = - getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS); - + // TODO(dborowitz): Just scan author space. + Set<String> refNames = getRefNamesAllUsers(RefNames.REFS_DRAFT_COMMENTS); List<PatchLineComment> comments = Lists.newArrayList(); for (String refName : refNames) { Account.Id id = Account.Id.fromRefPart(refName); @@ -257,10 +284,8 @@ continue; } Change.Id changeId = Change.Id.parse(refName); - DraftCommentNotes draftNotes = - draftFactory.create(changeId, author).load(); - comments.addAll(draftNotes.getDraftBaseComments().values()); - comments.addAll(draftNotes.getDraftPsComments().values()); + comments.addAll( + draftFactory.create(changeId, author).load().getComments().values()); } return sort(comments); } @@ -297,33 +322,45 @@ db.patchComments().delete(comments); } - private static Collection<PatchLineComment> addCommentsOnFile( - Collection<PatchLineComment> commentsOnFile, + private static List<PatchLineComment> commentsOnFile( Collection<PatchLineComment> allComments, String file) { + List<PatchLineComment> result = new ArrayList<>(allComments.size()); for (PatchLineComment c : allComments) { String currentFilename = c.getKey().getParentKey().getFileName(); if (currentFilename.equals(file)) { - commentsOnFile.add(c); + result.add(c); } } - return commentsOnFile; + return sort(result); } - public static void setCommentRevId(PatchLineComment c, + private static List<PatchLineComment> commentsOnPatchSet( + Collection<PatchLineComment> allComments, + PatchSet.Id psId) { + List<PatchLineComment> result = new ArrayList<>(allComments.size()); + for (PatchLineComment c : allComments) { + if (getCommentPsId(c).equals(psId)) { + result.add(c); + } + } + return sort(result); + } + + public static RevId setCommentRevId(PatchLineComment c, PatchListCache cache, Change change, PatchSet ps) throws OrmException { - if (c.getRevId() != null) { - return; + if (c.getRevId() == null) { + try { + // TODO(dborowitz): Bypass cache if side is REVISION. + PatchList patchList = cache.get(change, ps); + c.setRevId((c.getSide() == (short) 0) + ? new RevId(ObjectId.toString(patchList.getOldId())) + : new RevId(ObjectId.toString(patchList.getNewId()))); + } catch (PatchListNotAvailableException e) { + throw new OrmException(e); + } } - PatchList patchList; - try { - patchList = cache.get(change, ps); - } catch (PatchListNotAvailableException e) { - throw new OrmException(e); - } - c.setRevId((c.getSide() == (short) 0) - ? new RevId(ObjectId.toString(patchList.getOldId())) - : new RevId(ObjectId.toString(patchList.getNewId()))); + return c.getRevId(); } private Set<String> getRefNamesAllUsers(String prefix) throws OrmException { @@ -356,7 +393,7 @@ } private static List<PatchLineComment> sort(List<PatchLineComment> comments) { - Collections.sort(comments, ChangeNotes.PLC_ORDER); + Collections.sort(comments, PLC_ORDER); return comments; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java index 580897f..465c9ba 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java
@@ -263,7 +263,7 @@ } } - public class PermissionInfo { + public static class PermissionInfo { public String label; public Boolean exclusive; public Map<String, PermissionRuleInfo> rules; @@ -278,7 +278,7 @@ } } - public class PermissionRuleInfo { + public static class PermissionRuleInfo { public PermissionRule.Action action; public Boolean force; public Integer min;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java index 7031672..34f83f7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java
@@ -16,15 +16,44 @@ import com.google.common.base.Strings; import com.google.common.collect.Sets; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.Account.FieldName; import com.google.gerrit.reviewdb.client.AccountExternalId; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.mail.EmailSender; +import com.google.inject.Inject; import java.util.Collection; +import java.util.HashSet; import java.util.Objects; import java.util.Set; /** Basic implementation of {@link Realm}. */ public abstract class AbstractRealm implements Realm { + private EmailSender emailSender; + + @Inject(optional = true) + void setEmailSender(EmailSender emailSender) { + this.emailSender = emailSender; + } + + @Override + public Set<FieldName> getEditableFields() { + Set<Account.FieldName> fields = new HashSet<>(); + for (Account.FieldName n : Account.FieldName.values()) { + if (allowsEdit(n)) { + if (n == Account.FieldName.REGISTER_NEW_EMAIL) { + if (emailSender != null && emailSender.isEnabled()) { + fields.add(n); + } + } else { + fields.add(n); + } + } + } + return fields; + } + @Override public boolean hasEmailAddress(IdentifiedUser user, String email) { for (AccountExternalId ext : user.state().getExternalIds()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java index 445ac6e..b4ca530 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -39,7 +39,7 @@ USERNAME, /** Numeric account ID, may be deprecated. */ - ID; + ID } public abstract void fillAccountInfo(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java index efe7322..80a451a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsCollection.java
@@ -77,9 +77,9 @@ * Parses a account ID from a request body and returns the user. * * @param id ID of the account, can be a string of the format - * "Full Name <email@example.com>", just the email address, a full name - * if it is unique, an account ID, a user name or 'self' for the - * calling user + * "{@code Full Name <email@example.com>}", just the email address, + * a full name if it is unique, an account ID, a user name or + * "{@code self}" for the calling user * @return the user, never null. * @throws UnprocessableEntityException thrown if the account ID cannot be * resolved or if the account is not visible to the calling user @@ -102,9 +102,9 @@ * check whether the current user can see the account. * * @param id ID of the account, can be a string of the format - * "Full Name <email@example.com>", just the email address, a full name - * if it is unique, an account ID, a user name or 'self' for the - * calling user + * "{@code Full Name <email@example.com>}", just the email address, + * a full name if it is unique, an account ID, a user name or + * "{@code self}" for the calling user * @return the user, null if no user is found for the given account ID * @throws AuthException thrown if 'self' is used as account ID and the * current user is not authenticated
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateGroupArgs.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateGroupArgs.java index 807eed6..9ddef3a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateGroupArgs.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateGroupArgs.java
@@ -25,7 +25,6 @@ public boolean visibleToAll; public AccountGroup.Id ownerGroupId; public Collection<? extends Account.Id> initialMembers; - public Collection<? extends AccountGroup.UUID> initialGroups; public AccountGroup.NameKey getGroup() { return groupName;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java index d335add..f777145 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetGroups.java
@@ -16,12 +16,12 @@ import com.google.common.collect.Lists; import com.google.gerrit.common.errors.NoSuchGroupException; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.group.GroupJson; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Singleton;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java deleted file mode 100644 index fd1f33e..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java +++ /dev/null
@@ -1,158 +0,0 @@ -// Copyright (C) 2011 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License - -package com.google.gerrit.server.account; - -import com.google.gerrit.audit.AuditService; -import com.google.gerrit.common.errors.NameAlreadyUsedException; -import com.google.gerrit.common.errors.PermissionDeniedException; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.AccountGroupById; -import com.google.gerrit.reviewdb.client.AccountGroupMember; -import com.google.gerrit.reviewdb.client.AccountGroupName; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.GerritPersonIdent; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gwtorm.server.OrmDuplicateKeyException; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; - -import org.eclipse.jgit.lib.PersonIdent; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public class PerformCreateGroup { - - public interface Factory { - PerformCreateGroup create(CreateGroupArgs createGroupArgs); - } - - private final ReviewDb db; - private final AccountCache accountCache; - private final GroupIncludeCache groupIncludeCache; - private final IdentifiedUser currentUser; - private final PersonIdent serverIdent; - private final GroupCache groupCache; - private final CreateGroupArgs createGroupArgs; - private final AuditService auditService; - - @Inject - PerformCreateGroup(ReviewDb db, AccountCache accountCache, - GroupIncludeCache groupIncludeCache, IdentifiedUser currentUser, - @GerritPersonIdent PersonIdent serverIdent, GroupCache groupCache, - @Assisted CreateGroupArgs createGroupArgs, AuditService auditService) { - this.db = db; - this.accountCache = accountCache; - this.groupIncludeCache = groupIncludeCache; - this.currentUser = currentUser; - this.serverIdent = serverIdent; - this.groupCache = groupCache; - this.createGroupArgs = createGroupArgs; - this.auditService = auditService; - } - - /** - * Creates a new group. - - * @return the new group - * @throws OrmException is thrown in case of any data store read or write - * error - * @throws NameAlreadyUsedException is thrown in case a group with the given - * name already exists - * @throws PermissionDeniedException user cannot create a group. - */ - public AccountGroup createGroup() throws OrmException, - NameAlreadyUsedException, PermissionDeniedException { - if (!currentUser.getCapabilities().canCreateGroup()) { - throw new PermissionDeniedException(String.format( - "%s does not have \"Create Group\" capability.", - currentUser.getUserName())); - } - AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId()); - AccountGroup.UUID uuid = GroupUUID.make( - createGroupArgs.getGroupName(), - currentUser.newCommitterIdent( - serverIdent.getWhen(), - serverIdent.getTimeZone())); - AccountGroup group = - new AccountGroup(createGroupArgs.getGroup(), groupId, uuid); - group.setVisibleToAll(createGroupArgs.visibleToAll); - if (createGroupArgs.ownerGroupId != null) { - AccountGroup ownerGroup = groupCache.get(createGroupArgs.ownerGroupId); - if (ownerGroup != null) { - group.setOwnerGroupUUID(ownerGroup.getGroupUUID()); - } - } - if (createGroupArgs.groupDescription != null) { - group.setDescription(createGroupArgs.groupDescription); - } - AccountGroupName gn = new AccountGroupName(group); - // first insert the group name to validate that the group name hasn't - // already been used to create another group - try { - db.accountGroupNames().insert(Collections.singleton(gn)); - } catch (OrmDuplicateKeyException e) { - throw new NameAlreadyUsedException(createGroupArgs.getGroupName()); - } - db.accountGroups().insert(Collections.singleton(group)); - - addMembers(groupId, createGroupArgs.initialMembers); - - if (createGroupArgs.initialGroups != null) { - addGroups(groupId, createGroupArgs.initialGroups); - groupIncludeCache.evictSubgroupsOf(uuid); - } - - groupCache.onCreateGroup(createGroupArgs.getGroup()); - - return group; - } - - private void addMembers(final AccountGroup.Id groupId, - final Collection<? extends Account.Id> members) throws OrmException { - List<AccountGroupMember> memberships = new ArrayList<>(); - for (Account.Id accountId : members) { - final AccountGroupMember membership = - new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)); - memberships.add(membership); - } - db.accountGroupMembers().insert(memberships); - auditService.dispatchAddAccountsToGroup(currentUser.getAccountId(), memberships); - - for (Account.Id accountId : members) { - accountCache.evict(accountId); - } - } - - private void addGroups(final AccountGroup.Id groupId, - final Collection<? extends AccountGroup.UUID> groups) throws OrmException { - List<AccountGroupById> includeList = new ArrayList<>(); - for (AccountGroup.UUID includeUUID : groups) { - final AccountGroupById groupInclude = - new AccountGroupById(new AccountGroupById.Key(groupId, includeUUID)); - includeList.add(groupInclude); - } - db.accountGroupById().insert(includeList); - auditService.dispatchAddGroupsToGroup(currentUser.getAccountId(), includeList); - - for (AccountGroup.UUID uuid : groups) { - groupIncludeCache.evictParentGroupsOf(uuid); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformRenameGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformRenameGroup.java deleted file mode 100644 index 3623cc3..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformRenameGroup.java +++ /dev/null
@@ -1,125 +0,0 @@ -// Copyright (C) 2011 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License - -package com.google.gerrit.server.account; - -import com.google.gerrit.common.data.GroupDetail; -import com.google.gerrit.common.errors.InvalidNameException; -import com.google.gerrit.common.errors.NameAlreadyUsedException; -import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.AccountGroupName; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.git.RenameGroupOp; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; - -import java.util.Collections; -import java.util.Date; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -public class PerformRenameGroup { - - public interface Factory { - PerformRenameGroup create(); - } - - private final ReviewDb db; - private final GroupCache groupCache; - private final GroupControl.Factory groupControlFactory; - private final GroupDetailFactory.Factory groupDetailFactory; - private final RenameGroupOp.Factory renameGroupOpFactory; - private final IdentifiedUser currentUser; - - @Inject - PerformRenameGroup(final ReviewDb db, final GroupCache groupCache, - final GroupControl.Factory groupControlFactory, - final GroupDetailFactory.Factory groupDetailFactory, - final RenameGroupOp.Factory renameGroupOpFactory, - final IdentifiedUser currentUser) { - this.db = db; - this.groupCache = groupCache; - this.groupControlFactory = groupControlFactory; - this.groupDetailFactory = groupDetailFactory; - this.renameGroupOpFactory = renameGroupOpFactory; - this.currentUser = currentUser; - } - - public GroupDetail renameGroup(final String groupName, - final String newGroupName) throws OrmException, NameAlreadyUsedException, - NoSuchGroupException, InvalidNameException { - final AccountGroup.NameKey groupNameKey = - new AccountGroup.NameKey(groupName); - final AccountGroup group = groupCache.get(groupNameKey); - if (group == null) { - throw new NoSuchGroupException(groupNameKey); - } - return renameGroup(group.getId(), newGroupName); - } - - public GroupDetail renameGroup(final AccountGroup.Id groupId, - final String newName) throws OrmException, NameAlreadyUsedException, - NoSuchGroupException, InvalidNameException { - final GroupControl ctl = groupControlFactory.validateFor(groupId); - final AccountGroup group = db.accountGroups().get(groupId); - if (group == null || !ctl.isOwner()) { - throw new NoSuchGroupException(groupId); - } - if (newName.trim().isEmpty()) { - throw new InvalidNameException(); - } - - final AccountGroup.NameKey old = group.getNameKey(); - final AccountGroup.NameKey key = new AccountGroup.NameKey(newName); - - try { - final AccountGroupName id = new AccountGroupName(key, groupId); - db.accountGroupNames().insert(Collections.singleton(id)); - } catch (OrmException e) { - AccountGroupName other = db.accountGroupNames().get(key); - if (other != null) { - // If we are using this identity, don't report the exception. - // - if (other.getId().equals(groupId)) { - return groupDetailFactory.create(groupId).call(); - } - - // Otherwise, someone else has this identity. - // - throw new NameAlreadyUsedException(newName); - } else { - throw e; - } - } - - group.setNameKey(key); - db.accountGroups().update(Collections.singleton(group)); - - AccountGroupName priorName = db.accountGroupNames().get(old); - if (priorName != null) { - db.accountGroupNames().delete(Collections.singleton(priorName)); - } - - groupCache.evict(group); - groupCache.evictAfterRename(old, key); - renameGroupOpFactory.create( // - currentUser.newCommitterIdent(new Date(), TimeZone.getDefault()), // - group.getGroupUUID(), // - old.get(), newName).start(0, TimeUnit.MILLISECONDS); - - return groupDetailFactory.create(groupId).call(); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java index 8dd8de7..056fa85b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -24,6 +24,9 @@ /** Can the end-user modify this field of their own account? */ public boolean allowsEdit(Account.FieldName field); + /** Returns the account fields that the end-user can modify. */ + public Set<Account.FieldName> getEditableFields(); + public AuthRequest authenticate(AuthRequest who) throws AccountException; public AuthRequest link(ReviewDb db, Account.Id to, AuthRequest who)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java index 07936d9..d181c35 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java
@@ -39,7 +39,7 @@ import java.util.List; import java.util.Map; -class SuggestAccounts implements RestReadView<TopLevelResource> { +public class SuggestAccounts implements RestReadView<TopLevelResource> { private static final int MAX_RESULTS = 100; private static final String MAX_SUFFIX = "\u9fa5"; @@ -50,9 +50,10 @@ private final int suggestFrom; private int limit = 10; + private String query; @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return") - void setLimit(int n) { + public void setLimit(int n) { if (n < 0) { limit = 10; } else if (n == 0) { @@ -63,7 +64,9 @@ } @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "match users") - private String query; + public void setQuery(String query) { + this.query = query; + } @Inject SuggestAccounts(AccountControl.Factory accountControlFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java index 9b32ca9..4a652b4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
@@ -73,6 +73,9 @@ @Override public GroupDescription.Basic get(AccountGroup.UUID uuid) { + if (uuid == null) { + return null; + } GroupBackend b = backend(uuid); if (b == null) { log.warn("Unknown GroupBackend for UUID: " + uuid); @@ -121,6 +124,9 @@ @Override public boolean contains(AccountGroup.UUID uuid) { + if (uuid == null) { + return false; + } GroupMembership m = membership(uuid); if (m == null) { log.warn("Unknown GroupMembership for UUID: " + uuid); @@ -134,6 +140,9 @@ Multimap<GroupMembership, AccountGroup.UUID> lookups = ArrayListMultimap.create(); for (AccountGroup.UUID uuid : uuids) { + if (uuid == null) { + continue; + } GroupMembership m = membership(uuid); if (m == null) { log.warn("Unknown GroupMembership for UUID: " + uuid); @@ -161,6 +170,9 @@ Multimap<GroupMembership, AccountGroup.UUID> lookups = ArrayListMultimap.create(); for (AccountGroup.UUID uuid : uuids) { + if (uuid == null) { + continue; + } GroupMembership m = membership(uuid); if (m == null) { log.warn("Unknown GroupMembership for UUID: " + uuid);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountPreferences.java index c4d4b06..da7d141 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountPreferences.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountPreferences.java
@@ -27,7 +27,6 @@ /** Preferences for user accounts. */ public class VersionedAccountPreferences extends VersionedMetaData { - private static final String REFS_USER_DEFAULT = RefNames.REFS_USER + "default"; private static final String PREFERENCES = "preferences.config"; public static VersionedAccountPreferences forUser(Account.Id id) { @@ -35,7 +34,7 @@ } public static VersionedAccountPreferences forDefault() { - return new VersionedAccountPreferences(REFS_USER_DEFAULT); + return new VersionedAccountPreferences(RefNames.REFS_USERS_DEFAULT); } private final String ref; @@ -46,7 +45,7 @@ } public boolean isDefaults() { - return REFS_USER_DEFAULT.equals(getRefName()); + return RefNames.REFS_USERS_DEFAULT.equals(getRefName()); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java new file mode 100644 index 0000000..b12e7ce --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
@@ -0,0 +1,71 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.account; + +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.RefNames; +import com.google.gerrit.server.git.QueryList; +import com.google.gerrit.server.git.ValidationError; +import com.google.gerrit.server.git.VersionedMetaData; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** Named Queries for user accounts. */ +public class VersionedAccountQueries extends VersionedMetaData { + private static final Logger log = LoggerFactory.getLogger(VersionedAccountQueries.class); + + public static VersionedAccountQueries forUser(Account.Id id) { + return new VersionedAccountQueries(RefNames.refsUsers(id)); + } + + private final String ref; + private QueryList queryList; + + private VersionedAccountQueries(String ref) { + this.ref = ref; + } + + @Override + protected String getRefName() { + return ref; + } + + public QueryList getQueryList() { + return queryList; + } + + @Override + protected void onLoad() throws IOException, ConfigInvalidException { + ValidationError.Sink errors = new ValidationError.Sink() { + @Override + public void error(ValidationError error) { + log.error("Error parsing file " + QueryList.FILE_NAME + ": " + + error.getMessage()); + } + }; + queryList = QueryList.parse(readUTF8(QueryList.FILE_NAME), errors); + } + + @Override + protected boolean onSave(CommitBuilder commit) throws IOException, + ConfigInvalidException { + throw new UnsupportedOperationException("Cannot yet save named queries"); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java index 5f581c3..ee7e1a5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/GerritApiImpl.java
@@ -17,22 +17,26 @@ import com.google.gerrit.extensions.api.GerritApi; import com.google.gerrit.extensions.api.accounts.Accounts; import com.google.gerrit.extensions.api.changes.Changes; +import com.google.gerrit.extensions.api.groups.Groups; import com.google.gerrit.extensions.api.projects.Projects; import com.google.inject.Inject; import com.google.inject.Singleton; @Singleton -class GerritApiImpl extends GerritApi.NotImplemented implements GerritApi { +class GerritApiImpl implements GerritApi { private final Accounts accounts; private final Changes changes; + private final Groups groups; private final Projects projects; @Inject GerritApiImpl(Accounts accounts, Changes changes, + Groups groups, Projects projects) { this.accounts = accounts; this.changes = changes; + this.groups = groups; this.projects = projects; } @@ -47,6 +51,11 @@ } @Override + public Groups groups() { + return groups; + } + + @Override public Projects projects() { return projects; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java index 23f4b8d..c686415 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/Module.java
@@ -22,8 +22,9 @@ protected void configure() { bind(GerritApi.class).to(GerritApiImpl.class); - install(new com.google.gerrit.server.api.changes.Module()); - install(new com.google.gerrit.server.api.projects.Module()); install(new com.google.gerrit.server.api.accounts.Module()); + install(new com.google.gerrit.server.api.changes.Module()); + install(new com.google.gerrit.server.api.groups.Module()); + install(new com.google.gerrit.server.api.projects.Module()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java index 44413b7..7051617 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -28,7 +28,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -public class AccountApiImpl extends AccountApi.NotImplemented implements AccountApi { +public class AccountApiImpl implements AccountApi { interface Factory { AccountApiImpl create(AccountResource account); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java index 0c02c99..3f578ae 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -16,6 +16,7 @@ import com.google.gerrit.extensions.api.accounts.AccountApi; import com.google.gerrit.extensions.api.accounts.Accounts; +import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; @@ -24,24 +25,30 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.AccountsCollection; +import com.google.gerrit.server.account.SuggestAccounts; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import java.util.List; + @Singleton -public class AccountsImpl extends Accounts.NotImplemented implements Accounts { +public class AccountsImpl implements Accounts { private final AccountsCollection accounts; private final AccountApiImpl.Factory api; private final Provider<CurrentUser> self; + private final Provider<SuggestAccounts> suggestAccountsProvider; @Inject AccountsImpl(AccountsCollection accounts, AccountApiImpl.Factory api, - Provider<CurrentUser> self) { + Provider<CurrentUser> self, + Provider<SuggestAccounts> suggestAccountsProvider) { this.accounts = accounts; this.api = api; this.self = self; + this.suggestAccountsProvider = suggestAccountsProvider; } @Override @@ -61,4 +68,32 @@ } return api.create(new AccountResource((IdentifiedUser)self.get())); } + + @Override + public SuggestAccountsRequest suggestAccounts() throws RestApiException { + return new SuggestAccountsRequest() { + @Override + public List<AccountInfo> get() throws RestApiException { + return AccountsImpl.this.suggestAccounts(this); + } + }; + } + + @Override + public SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException { + return suggestAccounts().withQuery(query); + } + + private List<AccountInfo> suggestAccounts(SuggestAccountsRequest r) + throws RestApiException { + try { + SuggestAccounts mySuggestAccounts = suggestAccountsProvider.get(); + mySuggestAccounts.setQuery(r.getQuery()); + mySuggestAccounts.setLimit(r.getLimit()); + return mySuggestAccounts.apply(TopLevelResource.INSTANCE); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve suggested accounts", e); + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java index 161461d..78b3f10 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -26,11 +26,14 @@ import com.google.gerrit.extensions.api.changes.RevisionApi; import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.SuggestedReviewerInfo; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.change.Abandon; import com.google.gerrit.server.change.ChangeEdits; import com.google.gerrit.server.change.ChangeJson; @@ -38,6 +41,8 @@ import com.google.gerrit.server.change.Check; import com.google.gerrit.server.change.GetHashtags; import com.google.gerrit.server.change.GetTopic; +import com.google.gerrit.server.change.ListChangeComments; +import com.google.gerrit.server.change.ListChangeDrafts; import com.google.gerrit.server.change.PostHashtags; import com.google.gerrit.server.change.PostReviewers; import com.google.gerrit.server.change.PutTopic; @@ -54,13 +59,15 @@ import java.io.IOException; import java.util.EnumSet; import java.util.List; +import java.util.Map; import java.util.Set; -class ChangeApiImpl extends ChangeApi.NotImplemented implements ChangeApi { +class ChangeApiImpl implements ChangeApi { interface Factory { ChangeApiImpl create(ChangeResource change); } + private final Provider<CurrentUser> user; private final Changes changeApi; private final Revisions revisions; private final RevisionApiImpl.Factory revisionApi; @@ -75,11 +82,14 @@ private final Provider<ChangeJson> changeJson; private final PostHashtags postHashtags; private final GetHashtags getHashtags; + private final ListChangeComments listComments; + private final ListChangeDrafts listDrafts; private final Check check; private final ChangeEdits.Detail editDetail; @Inject - ChangeApiImpl(Changes changeApi, + ChangeApiImpl(Provider<CurrentUser> user, + Changes changeApi, Revisions revisions, RevisionApiImpl.Factory revisionApi, Provider<SuggestReviewers> suggestReviewers, @@ -92,9 +102,12 @@ Provider<ChangeJson> changeJson, PostHashtags postHashtags, GetHashtags getHashtags, + ListChangeComments listComments, + ListChangeDrafts listDrafts, Check check, ChangeEdits.Detail editDetail, @Assisted ChangeResource change) { + this.user = user; this.changeApi = changeApi; this.revert = revert; this.revisions = revisions; @@ -108,6 +121,8 @@ this.changeJson = changeJson; this.postHashtags = postHashtags; this.getHashtags = getHashtags; + this.listComments = listComments; + this.listDrafts = listDrafts; this.check = check; this.editDetail = editDetail; this.change = change; @@ -244,6 +259,10 @@ public ChangeInfo get(EnumSet<ListChangesOption> s) throws RestApiException { try { + CurrentUser u = user.get(); + if (u.isIdentifiedUser()) { + ((IdentifiedUser) u).clearStarredChanges(); + } return changeJson.get().addOptions(s).format(change); } catch (OrmException e) { throw new RestApiException("Cannot retrieve change", e); @@ -289,6 +308,24 @@ } @Override + public Map<String, List<CommentInfo>> comments() throws RestApiException { + try { + return listComments.apply(change); + } catch (OrmException e) { + throw new RestApiException("Cannot get comments", e); + } + } + + @Override + public Map<String, List<CommentInfo>> drafts() throws RestApiException { + try { + return listDrafts.apply(change); + } catch (OrmException e) { + throw new RestApiException("Cannot get drafts", e); + } + } + + @Override public ChangeInfo check() throws RestApiException { try { return check.apply(change).value();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java index 91809ec..f7705a7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -24,11 +24,12 @@ import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.Url; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.change.ChangesCollection; import com.google.gerrit.server.change.CreateChange; import com.google.gerrit.server.project.InvalidChangeOperationException; @@ -43,16 +44,19 @@ @Singleton class ChangesImpl implements Changes { + private final Provider<CurrentUser> user; private final ChangesCollection changes; private final ChangeApiImpl.Factory api; private final CreateChange createChange; private final Provider<QueryChanges> queryProvider; @Inject - ChangesImpl(ChangesCollection changes, + ChangesImpl(Provider<CurrentUser> user, + ChangesCollection changes, ChangeApiImpl.Factory api, CreateChange createChange, Provider<QueryChanges> queryProvider) { + this.user = user; this.changes = changes; this.api = api; this.createChange = createChange; @@ -123,6 +127,10 @@ } try { + CurrentUser u = user.get(); + if (u.isIdentifiedUser()) { + ((IdentifiedUser) u).clearStarredChanges(); + } List<?> result = qc.apply(TopLevelResource.INSTANCE); if (result.isEmpty()) { return ImmutableList.of(); @@ -136,7 +144,7 @@ List<ChangeInfo> infos = (List<ChangeInfo>) result; return ImmutableList.copyOf(infos); - } catch (BadRequestException | AuthException | OrmException e) { + } catch (AuthException | OrmException e) { throw new RestApiException("Cannot query changes", e); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/FileApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/FileApiImpl.java index 42c1e23..c09890f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/FileApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/FileApiImpl.java
@@ -30,7 +30,7 @@ import java.io.IOException; -class FileApiImpl extends FileApi.NotImplemented implements FileApi { +class FileApiImpl implements FileApi { interface Factory { FileApiImpl create(FileResource r); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java index c36faa2..1b420f7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -27,6 +27,7 @@ import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.api.changes.RevisionApi; import com.google.gerrit.extensions.api.changes.SubmitInput; +import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.FileInfo; import com.google.gerrit.extensions.common.MergeableInfo; @@ -40,16 +41,17 @@ import com.google.gerrit.server.change.DraftComments; import com.google.gerrit.server.change.FileResource; import com.google.gerrit.server.change.Files; -import com.google.gerrit.server.change.ListComments; -import com.google.gerrit.server.change.ListDraftComments; +import com.google.gerrit.server.change.GetRevisionActions; +import com.google.gerrit.server.change.ListRevisionComments; +import com.google.gerrit.server.change.ListRevisionDrafts; import com.google.gerrit.server.change.Mergeable; import com.google.gerrit.server.change.PostReview; import com.google.gerrit.server.change.PublishDraftPatchSet; import com.google.gerrit.server.change.Rebase; +import com.google.gerrit.server.change.RebaseChange; import com.google.gerrit.server.change.Reviewed; import com.google.gerrit.server.change.RevisionResource; import com.google.gerrit.server.change.Submit; -import com.google.gerrit.server.changedetail.RebaseChange; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -60,7 +62,7 @@ import java.util.Map; import java.util.Set; -class RevisionApiImpl extends RevisionApi.NotImplemented implements RevisionApi { +class RevisionApiImpl implements RevisionApi { interface Factory { RevisionApiImpl create(RevisionResource r); } @@ -80,13 +82,14 @@ private final Provider<PostReview> review; private final Provider<Mergeable> mergeable; private final FileApiImpl.Factory fileApi; - private final ListComments listComments; - private final ListDraftComments listDrafts; + private final ListRevisionComments listComments; + private final ListRevisionDrafts listDrafts; private final CreateDraftComment createDraft; private final DraftComments drafts; private final DraftApiImpl.Factory draftFactory; private final Comments comments; private final CommentApiImpl.Factory commentFactory; + private final GetRevisionActions revisionActions; @Inject RevisionApiImpl(Changes changes, @@ -103,13 +106,14 @@ Provider<PostReview> review, Provider<Mergeable> mergeable, FileApiImpl.Factory fileApi, - ListComments listComments, - ListDraftComments listDrafts, + ListRevisionComments listComments, + ListRevisionDrafts listDrafts, CreateDraftComment createDraft, DraftComments drafts, DraftApiImpl.Factory draftFactory, Comments comments, CommentApiImpl.Factory commentFactory, + GetRevisionActions revisionActions, @Assisted RevisionResource r) { this.changes = changes; this.cherryPick = cherryPick; @@ -132,6 +136,7 @@ this.draftFactory = draftFactory; this.comments = comments; this.commentFactory = commentFactory; + this.revisionActions = revisionActions; this.revision = r; } @@ -188,7 +193,7 @@ public ChangeApi rebase(RebaseInput in) throws RestApiException { try { return changes.id(rebase.apply(revision, in)._number); - } catch (OrmException | EmailException e) { + } catch (OrmException | EmailException | IOException e) { throw new RestApiException("Cannot rebase ps", e); } } @@ -293,6 +298,15 @@ } @Override + public List<CommentInfo> commentsAsList() throws RestApiException { + try { + return listComments.getComments(revision); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve comments", e); + } + } + + @Override public Map<String, List<CommentInfo>> drafts() throws RestApiException { try { return listDrafts.apply(revision); @@ -302,6 +316,15 @@ } @Override + public List<CommentInfo> draftsAsList() throws RestApiException { + try { + return listDrafts.getComments(revision); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve drafts", e); + } + } + + @Override public DraftApi draft(String id) throws RestApiException { try { return draftFactory.create(drafts.parse(revision, @@ -314,7 +337,11 @@ @Override public DraftApi createDraft(DraftInput in) throws RestApiException { try { - return draft(createDraft.apply(revision, in).value().id); + String id = createDraft.apply(revision, in).value().id; + // Reread change to pick up new notes refs. + return changes.id(revision.getChange().getId().get()) + .revision(revision.getPatchSet().getId().get()) + .draft(id); } catch (IOException | OrmException e) { throw new RestApiException("Cannot create draft", e); } @@ -329,4 +356,9 @@ throw new RestApiException("Cannot retrieve comment", e); } } + + @Override + public Map<String, ActionInfo> actions() throws RestApiException { + return revisionActions.apply(revision).value(); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java new file mode 100644 index 0000000..b7c8cd9 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -0,0 +1,261 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.api.groups; + +import com.google.gerrit.common.errors.NoSuchGroupException; +import com.google.gerrit.extensions.api.groups.GroupApi; +import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.common.GroupOptionsInfo; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.group.AddIncludedGroups; +import com.google.gerrit.server.group.AddMembers; +import com.google.gerrit.server.group.DeleteIncludedGroups; +import com.google.gerrit.server.group.DeleteMembers; +import com.google.gerrit.server.group.GetDescription; +import com.google.gerrit.server.group.GetDetail; +import com.google.gerrit.server.group.GetGroup; +import com.google.gerrit.server.group.GetName; +import com.google.gerrit.server.group.GetOptions; +import com.google.gerrit.server.group.GetOwner; +import com.google.gerrit.server.group.GroupResource; +import com.google.gerrit.server.group.ListIncludedGroups; +import com.google.gerrit.server.group.ListMembers; +import com.google.gerrit.server.group.PutDescription; +import com.google.gerrit.server.group.PutName; +import com.google.gerrit.server.group.PutOptions; +import com.google.gerrit.server.group.PutOwner; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; + +import java.util.Arrays; +import java.util.List; + +class GroupApiImpl implements GroupApi { + interface Factory { + GroupApiImpl create(GroupResource rsrc); + } + + private final GetGroup getGroup; + private final GetDetail getDetail; + private final GetName getName; + private final PutName putName; + private final GetOwner getOwner; + private final PutOwner putOwner; + private final GetDescription getDescription; + private final PutDescription putDescription; + private final GetOptions getOptions; + private final PutOptions putOptions; + private final Provider<ListMembers> listMembers; + private final AddMembers addMembers; + private final DeleteMembers deleteMembers; + private final ListIncludedGroups listGroups; + private final AddIncludedGroups addGroups; + private final DeleteIncludedGroups deleteGroups; + private final GroupResource rsrc; + + @AssistedInject + GroupApiImpl( + GetGroup getGroup, + GetDetail getDetail, + GetName getName, + PutName putName, + GetOwner getOwner, + PutOwner putOwner, + GetDescription getDescription, + PutDescription putDescription, + GetOptions getOptions, + PutOptions putOptions, + Provider<ListMembers> listMembers, + AddMembers addMembers, + DeleteMembers deleteMembers, + ListIncludedGroups listGroups, + AddIncludedGroups addGroups, + DeleteIncludedGroups deleteGroups, + @Assisted GroupResource rsrc) { + this.getGroup = getGroup; + this.getDetail = getDetail; + this.getName = getName; + this.putName = putName; + this.getOwner = getOwner; + this.putOwner = putOwner; + this.getDescription = getDescription; + this.putDescription = putDescription; + this.getOptions = getOptions; + this.putOptions = putOptions; + this.listMembers = listMembers; + this.addMembers = addMembers; + this.deleteMembers = deleteMembers; + this.listGroups = listGroups; + this.addGroups = addGroups; + this.deleteGroups = deleteGroups; + this.rsrc = rsrc; + } + + @Override + public GroupInfo get() throws RestApiException { + try { + return getGroup.apply(rsrc); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve group", e); + } + } + + @Override + public GroupInfo detail() throws RestApiException { + try { + return getDetail.apply(rsrc); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve group", e); + } + } + + @Override + public String name() throws RestApiException { + return getName.apply(rsrc); + } + + @Override + public void name(String name) throws RestApiException { + PutName.Input in = new PutName.Input(); + in.name = name; + try { + putName.apply(rsrc, in); + } catch (NoSuchGroupException e) { + throw new ResourceNotFoundException(name, e); + } catch (OrmException e) { + throw new RestApiException("Cannot put group name", e); + } + } + + @Override + public GroupInfo owner() throws RestApiException { + try { + return getOwner.apply(rsrc); + } catch (OrmException e) { + throw new RestApiException("Cannot get group owner", e); + } + } + + @Override + public void owner(String owner) throws RestApiException { + PutOwner.Input in = new PutOwner.Input(); + in.owner = owner; + try { + putOwner.apply(rsrc, in); + } catch (OrmException e) { + throw new RestApiException("Cannot put group owner", e); + } + } + + @Override + public String description() throws RestApiException { + return getDescription.apply(rsrc); + } + + @Override + public void description(String description) throws RestApiException { + PutDescription.Input in = new PutDescription.Input(); + in.description = description; + try { + putDescription.apply(rsrc, in); + } catch (OrmException e) { + throw new RestApiException("Cannot put group description", e); + } + } + + @Override + public GroupOptionsInfo options() throws RestApiException { + return getOptions.apply(rsrc); + } + + @Override + public void options(GroupOptionsInfo options) throws RestApiException { + try { + putOptions.apply(rsrc, options); + } catch (OrmException e) { + throw new RestApiException("Cannot put group options", e); + } + } + + @Override + public List<AccountInfo> members() throws RestApiException { + return members(false); + } + + @Override + public List<AccountInfo> members(boolean recursive) throws RestApiException { + ListMembers list = listMembers.get(); + list.setRecursive(recursive); + try { + return list.apply(rsrc); + } catch (OrmException e) { + throw new RestApiException("Cannot list group members", e); + } + } + + @Override + public void addMembers(String... members) throws RestApiException { + try { + addMembers.apply( + rsrc, AddMembers.Input.fromMembers(Arrays.asList(members))); + } catch (OrmException e) { + throw new RestApiException("Cannot add group members", e); + } + } + + @Override + public void removeMembers(String... members) throws RestApiException { + try { + deleteMembers.apply( + rsrc, AddMembers.Input.fromMembers(Arrays.asList(members))); + } catch (OrmException e) { + throw new RestApiException("Cannot remove group members", e); + } + } + + @Override + public List<GroupInfo> includedGroups() throws RestApiException { + try { + return listGroups.apply(rsrc); + } catch (OrmException e) { + throw new RestApiException("Cannot list included groups", e); + } + } + + @Override + public void addGroups(String... groups) throws RestApiException { + try { + addGroups.apply( + rsrc, AddIncludedGroups.Input.fromGroups(Arrays.asList(groups))); + } catch (OrmException e) { + throw new RestApiException("Cannot add group members", e); + } + } + + @Override + public void removeGroups(String... groups) throws RestApiException { + try { + deleteGroups.apply( + rsrc, AddIncludedGroups.Input.fromGroups(Arrays.asList(groups))); + } catch (OrmException e) { + throw new RestApiException("Cannot remove group members", e); + } + } + +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java new file mode 100644 index 0000000..97ba5d3 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -0,0 +1,147 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.api.groups; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability; + +import com.google.gerrit.extensions.api.groups.GroupApi; +import com.google.gerrit.extensions.api.groups.GroupInput; +import com.google.gerrit.extensions.api.groups.Groups; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.account.AccountsCollection; +import com.google.gerrit.server.group.CreateGroup; +import com.google.gerrit.server.group.GroupsCollection; +import com.google.gerrit.server.group.ListGroups; +import com.google.gerrit.server.project.ProjectsCollection; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import java.io.IOException; +import java.util.SortedMap; + +@Singleton +class GroupsImpl implements Groups { + private final AccountsCollection accounts; + private final GroupsCollection groups; + private final ProjectsCollection projects; + private final Provider<ListGroups> listGroups; + private final Provider<CurrentUser> user; + private final CreateGroup.Factory createGroup; + private final GroupApiImpl.Factory api; + + @Inject + GroupsImpl( + AccountsCollection accounts, + GroupsCollection groups, + ProjectsCollection projects, + Provider<ListGroups> listGroups, + Provider<CurrentUser> user, + CreateGroup.Factory createGroup, + GroupApiImpl.Factory api) { + this.accounts = accounts; + this.groups = groups; + this.projects = projects; + this.listGroups = listGroups; + this.user = user; + this.createGroup = createGroup; + this.api = api; + } + + @Override + public GroupApi id(String id) throws RestApiException { + return api.create( + groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(id))); + } + + @Override + public GroupApi create(String name) throws RestApiException { + GroupInput in = new GroupInput(); + in.name = name; + return create(in); + } + + @Override + public GroupApi create(GroupInput in) throws RestApiException { + if (checkNotNull(in, "GroupInput").name == null) { + throw new BadRequestException("GroupInput must specify name"); + } + checkRequiresCapability(user, null, CreateGroup.class); + try { + GroupInfo info = createGroup.create(in.name) + .apply(TopLevelResource.INSTANCE, in); + return id(info.id); + } catch (OrmException e) { + throw new RestApiException("Cannot create group " + in.name, e); + } + } + + @Override + public ListRequest list() { + return new ListRequest() { + @Override + public SortedMap<String, GroupInfo> getAsMap() throws RestApiException { + return list(this); + } + }; + } + + private SortedMap<String, GroupInfo> list(ListRequest req) + throws RestApiException { + TopLevelResource tlr = TopLevelResource.INSTANCE; + ListGroups list = listGroups.get(); + list.setOptions(req.getOptions()); + + for (String project : req.getProjects()) { + try { + list.addProject( + projects.parse(tlr, IdString.fromDecoded(project)).getControl()); + } catch (IOException e) { + throw new RestApiException("Error looking up project " + project, e); + } + } + + for (String group : req.getGroups()) { + list.addGroup(groups.parse(group).getGroupUUID()); + } + + list.setVisibleToAll(req.getVisibleToAll()); + + if (req.getUser() != null) { + try { + list.setUser(accounts.parse(req.getUser()).getAccountId()); + } catch (OrmException e) { + throw new RestApiException("Error looking up user " + req.getUser(), e); + } + } + + list.setOwned(req.getOwned()); + list.setLimit(req.getLimit()); + list.setStart(req.getStart()); + list.setMatchSubstring(req.getSubstring()); + try { + return list.apply(tlr); + } catch (OrmException e) { + throw new RestApiException("Cannot list groups", e); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java new file mode 100644 index 0000000..dae08cd --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java
@@ -0,0 +1,27 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.api.groups; + +import com.google.gerrit.extensions.api.groups.Groups; +import com.google.gerrit.server.config.FactoryModule; + +public class Module extends FactoryModule { + @Override + protected void configure() { + bind(Groups.class).to(GroupsImpl.class); + + factory(GroupApiImpl.Factory.class); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java index f4dc67e..0bb395f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
@@ -15,30 +15,41 @@ package com.google.gerrit.server.api.projects; import com.google.gerrit.extensions.api.projects.BranchApi; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.api.projects.BranchInput; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.project.BranchResource; +import com.google.gerrit.server.project.BranchesCollection; import com.google.gerrit.server.project.CreateBranch; +import com.google.gerrit.server.project.DeleteBranch; import com.google.gerrit.server.project.ProjectResource; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import java.io.IOException; -public class BranchApiImpl extends BranchApi.NotImplemented implements BranchApi { +public class BranchApiImpl implements BranchApi { interface Factory { BranchApiImpl create(ProjectResource project, String ref); } + private final BranchesCollection branches; private final CreateBranch.Factory createBranchFactory; + private final DeleteBranch deleteBranch; private final String ref; private final ProjectResource project; @Inject - BranchApiImpl( + BranchApiImpl(BranchesCollection branches, CreateBranch.Factory createBranchFactory, + DeleteBranch deleteBranch, @Assisted ProjectResource project, @Assisted String ref) { + this.branches = branches; this.createBranchFactory = createBranchFactory; + this.deleteBranch = deleteBranch; this.project = project; this.ref = ref; } @@ -55,4 +66,26 @@ throw new RestApiException("Cannot create branch", e); } } + + @Override + public BranchInfo get() throws RestApiException { + try { + return resource().getBranchInfo(); + } catch (IOException e) { + throw new RestApiException("Cannot read branch", e); + } + } + + @Override + public void delete() throws RestApiException { + try { + deleteBranch.apply(resource(), new DeleteBranch.Input()); + } catch (OrmException | IOException e) { + throw new RestApiException("Cannot delete branch", e); + } + } + + private BranchResource resource() throws RestApiException, IOException { + return branches.parse(project, IdString.fromDecoded(ref)); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java new file mode 100644 index 0000000..02dc919 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
@@ -0,0 +1,53 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.api.projects; + +import com.google.gerrit.extensions.api.projects.ChildProjectApi; +import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.project.ChildProjectResource; +import com.google.gerrit.server.project.GetChildProject; +import com.google.inject.Provider; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; + +public class ChildProjectApiImpl implements ChildProjectApi { + interface Factory { + ChildProjectApiImpl create(ChildProjectResource rsrc); + } + + private final Provider<GetChildProject> getProvider; + private final ChildProjectResource rsrc; + + @AssistedInject + ChildProjectApiImpl( + Provider<GetChildProject> getProvider, + @Assisted ChildProjectResource rsrc) { + this.getProvider = getProvider; + this.rsrc = rsrc; + } + + @Override + public ProjectInfo get() throws RestApiException { + return get(false); + } + + @Override + public ProjectInfo get(boolean recursive) throws RestApiException { + GetChildProject get = getProvider.get(); + get.setRecursive(recursive); + return get.apply(rsrc); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java index 0b7e258..2e6b761 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java
@@ -24,5 +24,6 @@ factory(BranchApiImpl.Factory.class); factory(ProjectApiImpl.Factory.class); + factory(ChildProjectApiImpl.Factory.class); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java index 7f73a38..84b219b5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -14,78 +14,122 @@ package com.google.gerrit.server.api.projects; -import com.google.common.base.Preconditions; -import com.google.gerrit.common.errors.ProjectCreationFailedException; +import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability; + import com.google.gerrit.extensions.api.projects.BranchApi; +import com.google.gerrit.extensions.api.projects.BranchInfo; +import com.google.gerrit.extensions.api.projects.ChildProjectApi; import com.google.gerrit.extensions.api.projects.ProjectApi; import com.google.gerrit.extensions.api.projects.ProjectInput; +import com.google.gerrit.extensions.api.projects.PutDescriptionInput; import com.google.gerrit.extensions.common.ProjectInfo; import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.TopLevelResource; -import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.project.ChildProjectsCollection; import com.google.gerrit.server.project.CreateProject; +import com.google.gerrit.server.project.GetDescription; +import com.google.gerrit.server.project.ListBranches; +import com.google.gerrit.server.project.ListChildProjects; import com.google.gerrit.server.project.ProjectJson; import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectsCollection; +import com.google.gerrit.server.project.PutDescription; import com.google.inject.Provider; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; -import java.io.IOException; +import org.eclipse.jgit.errors.ConfigInvalidException; -public class ProjectApiImpl extends ProjectApi.NotImplemented implements ProjectApi { +import java.io.IOException; +import java.util.List; + +public class ProjectApiImpl implements ProjectApi { interface Factory { ProjectApiImpl create(ProjectResource project); ProjectApiImpl create(String name); } + private final Provider<CurrentUser> user; private final Provider<CreateProject.Factory> createProjectFactory; private final ProjectApiImpl.Factory projectApi; private final ProjectsCollection projects; + private final GetDescription getDescription; + private final PutDescription putDescription; + private final ChildProjectApiImpl.Factory childApi; + private final ChildProjectsCollection children; private final ProjectResource project; private final ProjectJson projectJson; private final String name; private final BranchApiImpl.Factory branchApi; + private final Provider<ListBranches> listBranchesProvider; @AssistedInject - ProjectApiImpl(Provider<CreateProject.Factory> createProjectFactory, + ProjectApiImpl(Provider<CurrentUser> user, + Provider<CreateProject.Factory> createProjectFactory, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, + GetDescription getDescription, + PutDescription putDescription, + ChildProjectApiImpl.Factory childApi, + ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + Provider<ListBranches> listBranchesProvider, @Assisted ProjectResource project) { - this(createProjectFactory, projectApi, projects, projectJson, - branchApiFactory, project, null); + this(user, createProjectFactory, projectApi, projects, getDescription, + putDescription, childApi, children, projectJson, branchApiFactory, + listBranchesProvider, project, null); } @AssistedInject - ProjectApiImpl(Provider<CreateProject.Factory> createProjectFactory, + ProjectApiImpl(Provider<CurrentUser> user, + Provider<CreateProject.Factory> createProjectFactory, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, + GetDescription getDescription, + PutDescription putDescription, + ChildProjectApiImpl.Factory childApi, + ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + Provider<ListBranches> listBranchesProvider, @Assisted String name) { - this(createProjectFactory, projectApi, projects, projectJson, - branchApiFactory, null, name); + this(user, createProjectFactory, projectApi, projects, getDescription, + putDescription, childApi, children, projectJson, branchApiFactory, + listBranchesProvider, null, name); } - private ProjectApiImpl(Provider<CreateProject.Factory> createProjectFactory, + private ProjectApiImpl(Provider<CurrentUser> user, + Provider<CreateProject.Factory> createProjectFactory, ProjectApiImpl.Factory projectApi, ProjectsCollection projects, + GetDescription getDescription, + PutDescription putDescription, + ChildProjectApiImpl.Factory childApi, + ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + Provider<ListBranches> listBranchesProvider, ProjectResource project, String name) { + this.user = user; this.createProjectFactory = createProjectFactory; this.projectApi = projectApi; this.projects = projects; + this.getDescription = getDescription; + this.putDescription = putDescription; + this.childApi = childApi; + this.children = children; this.projectJson = projectJson; this.project = project; this.name = name; this.branchApi = branchApiFactory; + this.listBranchesProvider = listBranchesProvider; } @Override @@ -102,24 +146,93 @@ if (in.name != null && !name.equals(in.name)) { throw new BadRequestException("name must match input.name"); } + checkRequiresCapability(user, null, CreateProject.class); createProjectFactory.get().create(name) .apply(TopLevelResource.INSTANCE, in); return projectApi.create(projects.parse(name)); - } catch (BadRequestException | UnprocessableEntityException - | ResourceNotFoundException | ProjectCreationFailedException - | IOException e) { + } catch (IOException | ConfigInvalidException e) { throw new RestApiException("Cannot create project: " + e.getMessage(), e); } } @Override - public ProjectInfo get() { - Preconditions.checkNotNull(project); + public ProjectInfo get() throws RestApiException { + if (project == null) { + throw new ResourceNotFoundException(name); + } return projectJson.format(project); } @Override - public BranchApi branch(String ref) { - return branchApi.create(project, ref); + public String description() throws RestApiException { + return getDescription.apply(checkExists()); + } + + @Override + public void description(PutDescriptionInput in) + throws RestApiException { + try { + putDescription.apply(checkExists(), in); + } catch (IOException e) { + throw new RestApiException("Cannot put project description", e); + } + } + + @Override + public ListBranchesRequest branches() { + return new ListBranchesRequest() { + @Override + public List<BranchInfo> get() throws RestApiException { + return listBranches(this); + } + }; + } + + private List<BranchInfo> listBranches(ListBranchesRequest request) + throws RestApiException { + ListBranches list = listBranchesProvider.get(); + list.setLimit(request.getLimit()); + list.setStart(request.getStart()); + list.setMatchSubstring(request.getSubstring()); + list.setMatchRegex(request.getRegex()); + try { + return list.apply(checkExists()); + } catch (IOException e) { + throw new RestApiException("Cannot list branches", e); + } + } + + @Override + public List<ProjectInfo> children() throws RestApiException { + return children(false); + } + + @Override + public List<ProjectInfo> children(boolean recursive) throws RestApiException { + ListChildProjects list = children.list(); + list.setRecursive(recursive); + return list.apply(checkExists()); + } + + @Override + public ChildProjectApi child(String name) throws RestApiException { + try { + return childApi.create( + children.parse(checkExists(), IdString.fromDecoded(name))); + } catch (IOException e) { + throw new RestApiException("Cannot parse child project", e); + } + } + + @Override + public BranchApi branch(String ref) throws ResourceNotFoundException { + return branchApi.create(checkExists(), ref); + } + + private ProjectResource checkExists() throws ResourceNotFoundException { + if (project == null) { + throw new ResourceNotFoundException(name); + } + return project; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java index 86baa1e..db31d42 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -14,23 +14,25 @@ package com.google.gerrit.server.api.projects; -import com.google.common.collect.ImmutableList; import com.google.gerrit.extensions.api.projects.ProjectApi; +import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.api.projects.Projects; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.server.project.ListProjects; +import com.google.gerrit.server.project.ListProjects.FilterType; import com.google.gerrit.server.project.ProjectsCollection; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import java.io.IOException; -import java.util.List; +import java.util.SortedMap; @Singleton -class ProjectsImpl extends Projects.NotImplemented implements Projects { +class ProjectsImpl implements Projects { private final ProjectsCollection projects; private final ProjectApiImpl.Factory api; private final Provider<ListProjects> listProvider; @@ -56,22 +58,62 @@ } @Override + public ProjectApi create(String name) throws RestApiException { + ProjectInput in = new ProjectInput(); + in.name = name; + return create(in); + } + + @Override + public ProjectApi create(ProjectInput in) throws RestApiException { + return name(in.name).create(in); + } + + @Override public ListRequest list() { return new ListRequest() { @Override - public List<ProjectInfo> get() throws RestApiException { + public SortedMap<String, ProjectInfo> getAsMap() throws RestApiException { return list(this); } }; } - private List<ProjectInfo> list(ListRequest request) throws RestApiException { + private SortedMap<String, ProjectInfo> list(ListRequest request) + throws RestApiException { ListProjects lp = listProvider.get(); lp.setShowDescription(request.getDescription()); lp.setLimit(request.getLimit()); lp.setStart(request.getStart()); lp.setMatchPrefix(request.getPrefix()); - return ImmutableList.copyOf(lp.apply().values()); + lp.setMatchSubstring(request.getSubstring()); + lp.setMatchRegex(request.getRegex()); + lp.setShowTree(request.getShowTree()); + for (String branch : request.getBranches()) { + lp.addShowBranch(branch); + } + + FilterType type; + switch (request.getFilterType()) { + case ALL: + type = FilterType.ALL; + break; + case CODE: + type = FilterType.CODE; + break; + case PARENT_CANDIDATES: + type = FilterType.PARENT_CANDIDATES; + break; + case PERMISSIONS: + type = FilterType.PERMISSIONS; + break; + default: + throw new BadRequestException( + "Unknown filter type: " + request.getFilterType()); + } + lp.setFilterType(type); + + return lp.apply(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java index 1cbab8a..00eaf94 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -57,8 +57,7 @@ try { final Change.Key key = Change.Key.parse(tokens[2]); final Project.NameKey project = new Project.NameKey(tokens[0]); - final Branch.NameKey branch = - new Branch.NameKey(project, "refs/heads/" + tokens[1]); + final Branch.NameKey branch = new Branch.NameKey(project, tokens[1]); for (final ChangeData cd : queryProvider.get().byBranchKey(branch, key)) { setter.addValue(cd.getId()); return 1;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java index 9060108..3607e34 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -134,7 +134,7 @@ static List<String> optionalList(final Config config, final String name) { - String s[] = config.getStringList("ldap", null, name); + String[] s = config.getStringList("ldap", null, name); return Arrays.asList(s); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java index d0424d9..a73f8ba 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Abandon.java
@@ -15,7 +15,6 @@ package com.google.gerrit.server.change; import com.google.common.base.Strings; -import com.google.common.util.concurrent.CheckedFuture; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.extensions.api.changes.AbandonInput; import com.google.gerrit.extensions.common.ChangeInfo; @@ -123,17 +122,15 @@ } update.commit(); - CheckedFuture<?, IOException> indexFuture = - indexer.indexAsync(change.getId()); + indexer.index(db, change); try { - ReplyToChangeSender cm = abandonedSenderFactory.create(change); + ReplyToChangeSender cm = abandonedSenderFactory.create(change.getId()); cm.setFrom(caller.getAccountId()); cm.setChangeMessage(message); cm.send(); } catch (Exception e) { log.error("Cannot email update for change " + change.getChangeId(), e); } - indexFuture.checkedGet(); hooks.doChangeAbandonedHook(change, caller.getAccount(), db.patchSets().get(change.currentPatchSetId()),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ActionJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ActionJson.java index df6b76e..180b9ad 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ActionJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ActionJson.java
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription; import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.changedetail.RebaseChange; import com.google.gerrit.server.extensions.webui.UiActions; import com.google.gerrit.server.project.ChangeControl; import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java index a5054f3..14fa7d6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ArchiveFormat.java
@@ -19,14 +19,14 @@ import org.eclipse.jgit.archive.Tbz2Format; import org.eclipse.jgit.archive.TgzFormat; import org.eclipse.jgit.archive.TxzFormat; +import org.eclipse.jgit.archive.ZipFormat; public enum ArchiveFormat { TGZ("application/x-gzip", new TgzFormat()), TAR("application/x-tar", new TarFormat()), TBZ2("application/x-bzip2", new Tbz2Format()), - TXZ("application/x-xz", new TxzFormat()); - // Zip is not supported because it may be interpreted by a Java plugin as a - // valid JAR file, whose code would have access to cookies on the domain. + TXZ("application/x-xz", new TxzFormat()), + ZIP("application/x-zip", new ZipFormat()); private final ArchiveCommand.Format<?> format; private final String mimeType;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java index 9bd625d..73f9cab 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
@@ -140,7 +140,7 @@ return deleteFileFactory.create(id.get()); } - static class Create implements + public static class Create implements RestModifyView<ChangeResource, Put.Input> { interface Factory { @@ -195,7 +195,7 @@ } } - static class DeleteFile implements + public static class DeleteFile implements RestModifyView<ChangeResource, DeleteFile.Input> { public static class Input { } @@ -407,7 +407,7 @@ * as reverting or restoring a file to its previous contents. */ @Singleton - static class DeleteContent implements + public static class DeleteContent implements RestModifyView<ChangeEditResource, DeleteContent.Input> { public static class Input { } @@ -432,7 +432,7 @@ } @Singleton - static class Get implements RestReadView<ChangeEditResource> { + public static class Get implements RestReadView<ChangeEditResource> { private final FileContentUtil fileContentUtil; @Inject @@ -455,7 +455,7 @@ } @Singleton - static class GetMeta implements RestReadView<ChangeEditResource> { + public static class GetMeta implements RestReadView<ChangeEditResource> { private final WebLinks webLinks; @Inject @@ -481,8 +481,8 @@ return r; } - static class FileInfo { - List<DiffWebLinkInfo> webLinks; + public static class FileInfo { + public List<DiffWebLinkInfo> webLinks; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java index ff833f4..9bd6697 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -233,10 +233,10 @@ } CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId()); - if (!messageIsForChange()) { commitMessageNotForChange(); } + f.checkedGet(); if (sendMail) { Runnable sender = new Runnable() { @@ -244,7 +244,7 @@ public void run() { try { CreateChangeSender cm = - createChangeSenderFactory.create(change); + createChangeSenderFactory.create(change.getId()); cm.setFrom(change.getOwner()); cm.setPatchSet(patchSet, patchSetInfo); cm.addReviewers(reviewers); @@ -266,7 +266,6 @@ sender.run(); } } - f.checkedGet(); gitRefUpdated.fire(change.getProject(), patchSet.getRefName(), ObjectId.zeroId(), commit);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java index 4bcc82e..28614f7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -19,6 +19,7 @@ import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS; import static com.google.gerrit.extensions.client.ListChangesOption.CHANGE_ACTIONS; import static com.google.gerrit.extensions.client.ListChangesOption.CHECK; +import static com.google.gerrit.extensions.client.ListChangesOption.COMMIT_FOOTERS; import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_ACTIONS; import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_COMMIT; import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_FILES; @@ -31,12 +32,14 @@ import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES; import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWED; import static com.google.gerrit.extensions.client.ListChangesOption.WEB_LINKS; +import static com.google.gerrit.server.CommonConverters.toGitPerson; import com.google.auto.value.AutoValue; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; +import com.google.common.base.Throwables; import com.google.common.collect.FluentIterable; import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashMultimap; @@ -65,7 +68,6 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo; import com.google.gerrit.extensions.common.CommitInfo; import com.google.gerrit.extensions.common.FetchInfo; -import com.google.gerrit.extensions.common.GitPerson; import com.google.gerrit.extensions.common.LabelInfo; import com.google.gerrit.extensions.common.ProblemInfo; import com.google.gerrit.extensions.common.RevisionInfo; @@ -80,10 +82,7 @@ import com.google.gerrit.reviewdb.client.Patch; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; -import com.google.gerrit.reviewdb.client.PatchSetInfo; -import com.google.gerrit.reviewdb.client.PatchSetInfo.ParentInfo; import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.UserIdentity; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.ChangeMessagesUtil; @@ -92,13 +91,13 @@ import com.google.gerrit.server.PatchLineCommentsUtil; import com.google.gerrit.server.WebLinks; import com.google.gerrit.server.account.AccountLoader; -import com.google.gerrit.server.changedetail.RebaseChange; +import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.LabelNormalizer; +import com.google.gerrit.server.git.MergeUtil; import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.patch.PatchListNotAvailableException; -import com.google.gerrit.server.patch.PatchSetInfoFactory; -import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; import com.google.gerrit.server.project.ChangeControl; +import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.SubmitRuleEvaluator; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData.ChangedLines; @@ -107,10 +106,16 @@ import com.google.inject.Inject; import com.google.inject.Provider; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -130,9 +135,11 @@ private final LabelNormalizer labelNormalizer; private final Provider<CurrentUser> userProvider; private final AnonymousUser anonymous; + private final GitRepositoryManager repoManager; + private final ProjectCache projectCache; + private final MergeUtil.Factory mergeUtilFactory; private final IdentifiedUser.GenericFactory userFactory; private final ChangeData.Factory changeDataFactory; - private final PatchSetInfoFactory patchSetInfoFactory; private final FileInfoJson fileInfoJson; private final AccountLoader.Factory accountLoaderFactory; private final DynamicMap<DownloadScheme> downloadSchemes; @@ -154,9 +161,11 @@ LabelNormalizer ln, Provider<CurrentUser> user, AnonymousUser au, + GitRepositoryManager repoManager, + ProjectCache projectCache, + MergeUtil.Factory mergeUtilFactory, IdentifiedUser.GenericFactory uf, ChangeData.Factory cdf, - PatchSetInfoFactory psi, FileInfoJson fileInfoJson, AccountLoader.Factory ailf, DynamicMap<DownloadScheme> downloadSchemes, @@ -171,9 +180,11 @@ this.labelNormalizer = ln; this.userProvider = user; this.anonymous = au; - this.userFactory = uf; this.changeDataFactory = cdf; - this.patchSetInfoFactory = psi; + this.repoManager = repoManager; + this.userFactory = uf; + this.projectCache = projectCache; + this.mergeUtilFactory = mergeUtilFactory; this.fileInfoJson = fileInfoJson; this.accountLoaderFactory = ailf; this.downloadSchemes = downloadSchemes; @@ -238,9 +249,11 @@ ChangeInfo res = toChangeInfo(cd, reviewed, limitToPsId); accountLoader.fill(); return res; - } catch (OrmException | RuntimeException e) { + } catch (PatchListNotAvailableException | OrmException | IOException + | RuntimeException e) { if (!has(CHECK)) { - throw e; + Throwables.propagateIfPossible(e, OrmException.class); + throw new OrmException(e); } return checkOnly(cd); } @@ -298,7 +311,8 @@ if (i == null) { try { i = toChangeInfo(cd, reviewed, Optional.<PatchSet.Id> absent()); - } catch (OrmException | RuntimeException e) { + } catch (PatchListNotAvailableException | OrmException | IOException + | RuntimeException e) { if (has(CHECK)) { i = checkOnly(cd); } else { @@ -341,7 +355,8 @@ } private ChangeInfo toChangeInfo(ChangeData cd, Set<Change.Id> reviewed, - Optional<PatchSet.Id> limitToPsId) throws OrmException { + Optional<PatchSet.Id> limitToPsId) + throws PatchListNotAvailableException, OrmException, IOException { ChangeInfo out = new ChangeInfo(); if (has(CHECK)) { @@ -412,7 +427,7 @@ finish(out); if (needRevisions) { - out.revisions = revisions(ctl, cd, src); + out.revisions = revisions(ctl, src); if (out.revisions != null) { for (Map.Entry<String, RevisionInfo> entry : out.revisions.entrySet()) { if (entry.getValue().isCurrent) { @@ -830,14 +845,15 @@ return false; } - private Map<String, RevisionInfo> revisions(ChangeControl ctl, ChangeData cd, - Map<PatchSet.Id, PatchSet> map) throws OrmException { + private Map<String, RevisionInfo> revisions(ChangeControl ctl, + Map<PatchSet.Id, PatchSet> map) + throws PatchListNotAvailableException, OrmException, IOException { Map<String, RevisionInfo> res = Maps.newLinkedHashMap(); for (PatchSet in : map.values()) { if ((has(ALL_REVISIONS) - || in.getId().equals(cd.change().currentPatchSetId())) + || in.getId().equals(ctl.getChange().currentPatchSetId())) && ctl.isPatchVisible(in, db.get())) { - res.put(in.getRevision().get(), toRevisionInfo(ctl, cd, in)); + res.put(in.getRevision().get(), toRevisionInfo(ctl, in)); } } return res; @@ -847,11 +863,11 @@ Optional<PatchSet.Id> limitToPsId) throws OrmException { Collection<PatchSet> src; if (has(ALL_REVISIONS) || has(MESSAGES)) { - src = cd.patches(); + src = cd.patchSets(); } else { PatchSet ps; if (limitToPsId.isPresent()) { - ps = cd.patch(limitToPsId.get()); + ps = cd.patchSet(limitToPsId.get()); if (ps == null) { throw new OrmException("missing patch set " + limitToPsId.get()); } @@ -871,10 +887,11 @@ return map; } - private RevisionInfo toRevisionInfo(ChangeControl ctl, ChangeData cd, - PatchSet in) throws OrmException { + private RevisionInfo toRevisionInfo(ChangeControl ctl, PatchSet in) + throws PatchListNotAvailableException, OrmException, IOException { + Change c = ctl.getChange(); RevisionInfo out = new RevisionInfo(); - out.isCurrent = in.getId().equals(cd.change().currentPatchSetId()); + out.isCurrent = in.getId().equals(c.currentPatchSetId()); out._number = in.getId().get(); out.ref = in.getRefName(); out.created = in.getCreatedOn(); @@ -882,21 +899,30 @@ out.draft = in.isDraft() ? true : null; out.fetch = makeFetchMap(ctl, in); - if (has(ALL_COMMITS) || (out.isCurrent && has(CURRENT_COMMIT))) { - try { - out.commit = toCommit(in, cd.change().getProject(), has(WEB_LINKS)); - } catch (PatchSetInfoNotAvailableException e) { - throw new OrmException(e); + boolean setCommit = has(ALL_COMMITS) + || (out.isCurrent && has(CURRENT_COMMIT)); + boolean addFooters = out.isCurrent && has(COMMIT_FOOTERS); + if (setCommit || addFooters) { + Project.NameKey project = c.getProject(); + try (Repository repo = repoManager.openRepository(project); + RevWalk rw = new RevWalk(repo)) { + String rev = in.getRevision().get(); + RevCommit commit = rw.parseCommit(ObjectId.fromString(rev)); + rw.parseBody(commit); + if (setCommit) { + out.commit = toCommit(ctl, rw, commit, has(WEB_LINKS)); + } + if (addFooters) { + out.commitWithFooters = mergeUtilFactory + .create(projectCache.get(project)) + .createCherryPickCommitMessage(commit, ctl, in.getId()); + } } } if (has(ALL_FILES) || (out.isCurrent && has(CURRENT_FILES))) { - try { - out.files = fileInfoJson.toFileInfoMap(cd.change(), in); - out.files.remove(Patch.COMMIT_MSG); - } catch (PatchListNotAvailableException e) { - throw new OrmException(e); - } + out.files = fileInfoJson.toFileInfoMap(c, in); + out.files.remove(Patch.COMMIT_MSG); } if ((out.isCurrent || (out.draft != null && out.draft)) @@ -919,34 +945,35 @@ return out; } - CommitInfo toCommit(PatchSet in, Project.NameKey project, boolean addLinks) - throws PatchSetInfoNotAvailableException { - PatchSetInfo info = patchSetInfoFactory.get(db.get(), in.getId()); - CommitInfo commit = new CommitInfo(); - commit.parents = Lists.newArrayListWithCapacity(info.getParents().size()); - commit.author = toGitPerson(info.getAuthor()); - commit.committer = toGitPerson(info.getCommitter()); - commit.subject = info.getSubject(); - commit.message = info.getMessage(); + CommitInfo toCommit(ChangeControl ctl, RevWalk rw, RevCommit commit, + boolean addLinks) throws IOException { + Project.NameKey project = ctl.getChange().getProject(); + CommitInfo info = new CommitInfo(); + info.parents = new ArrayList<>(commit.getParentCount()); + info.author = toGitPerson(commit.getAuthorIdent()); + info.committer = toGitPerson(commit.getCommitterIdent()); + info.subject = commit.getShortMessage(); + info.message = commit.getFullMessage(); if (addLinks) { FluentIterable<WebLinkInfo> links = - webLinks.getPatchSetLinks(project, in.getRevision().get()); - commit.webLinks = links.isEmpty() ? null : links.toList(); + webLinks.getPatchSetLinks(project, commit.name()); + info.webLinks = links.isEmpty() ? null : links.toList(); } - for (ParentInfo parent : info.getParents()) { + for (RevCommit parent : commit.getParents()) { + rw.parseBody(parent); CommitInfo i = new CommitInfo(); - i.commit = parent.id.get(); - i.subject = parent.shortMessage; + i.commit = parent.name(); + i.subject = parent.getShortMessage(); if (addLinks) { FluentIterable<WebLinkInfo> parentLinks = - webLinks.getPatchSetLinks(project, parent.id.get()); + webLinks.getPatchSetLinks(project, parent.name()); i.webLinks = parentLinks.isEmpty() ? null : parentLinks.toList(); } - commit.parents.add(i); + info.parents.add(i); } - return commit; + return info; } private Map<String, FetchInfo> makeFetchMap(ChangeControl ctl, PatchSet in) @@ -1002,15 +1029,6 @@ fetchInfo.commands.put(commandName, c); } - private static GitPerson toGitPerson(UserIdentity committer) { - GitPerson p = new GitPerson(); - p.name = committer.getName(); - p.email = committer.getEmail(); - p.date = committer.getDate(); - p.tz = committer.getTimeZone(); - return p; - } - static void finish(ChangeInfo info) { info.id = Joiner.on('~').join( Url.encode(info.project),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java index d22d6ff..6e6f6fa 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
@@ -26,5 +26,5 @@ NO_CODE_CHANGE, /** Same tree, parent tree, same commit message. */ - NO_CHANGE; + NO_CHANGE }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java index 23039aa..8fdd445 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -317,7 +317,7 @@ repo = repoManager.openRepository(change.getProject()); ChangeData cd = changeDataFactory.create(db, change); - Collection<PatchSet> patchSetCollection = cd.patches(); + Collection<PatchSet> patchSetCollection = cd.patchSets(); PatchSet priorPs = patch; for (PatchSet ps : patchSetCollection) { if (ps.getId().get() < patch.getId().get() &&
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java index ff4a9f7..265cb49 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java
@@ -23,7 +23,6 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.changedetail.RebaseChange; import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ProjectState; @@ -61,18 +60,16 @@ return getControl().getNotes(); } - @Override - public String getETag() { - CurrentUser user = control.getCurrentUser(); - Hasher h = Hashing.md5().newHasher() - .putLong(getChange().getLastUpdatedOn().getTime()) + + // This includes all information relevant for ETag computation + // unrelated to the UI. + public void prepareETag(Hasher h, CurrentUser user) { + h.putLong(getChange().getLastUpdatedOn().getTime()) .putInt(getChange().getRowVersion()) - .putBoolean(user.getStarredChanges().contains(getChange().getId())) .putInt(user.isIdentifiedUser() ? ((IdentifiedUser) user).getAccountId().get() : 0) .putBoolean(rebaseChange != null && rebaseChange.canRebase(this)); - byte[] buf = new byte[20]; ObjectId noteId; try { @@ -87,6 +84,14 @@ for (ProjectState p : control.getProjectControl().getProjectState().tree()) { hashObjectId(h, p.getConfig().getRevision(), buf); } + } + + @Override + public String getETag() { + CurrentUser user = control.getCurrentUser(); + Hasher h = Hashing.md5().newHasher() + .putBoolean(user.getStarredChanges().contains(getChange().getId())); + prepareETag(h, user); return h.hash().toString(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java index 45bb1d4..7069e6d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeTriplet.java
@@ -21,8 +21,6 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; -import org.eclipse.jgit.lib.Constants; - @AutoValue public abstract class ChangeTriplet { public static String format(Change change) { @@ -53,10 +51,6 @@ String branch = Url.decode(triplet.substring(t1 + 1, t2)); String changeId = Url.decode(triplet.substring(t2 + 1)); - if (!branch.startsWith(Constants.R_REFS)) { - branch = Constants.R_HEADS + branch; - } - ChangeTriplet result = new AutoValue_ChangeTriplet( new Branch.NameKey(new Project.NameKey(project), branch), new Change.Key(changeId));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java index 42f16a3..6540ef2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -26,7 +26,6 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.changedetail.RebaseChange; import com.google.gerrit.server.index.ChangeIndexer; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchChangeException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java index efcd6d9..f4d1e0a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
@@ -102,6 +102,7 @@ return new UiAction.Description() .setLabel("Cherry Pick") .setTitle("Cherry pick change to a different branch") - .setVisible(resource.getControl().getProjectControl().canUpload()); + .setVisible(resource.getControl().getProjectControl().canUpload() + && resource.isCurrent()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java index b386894..b8e1178 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -14,6 +14,7 @@ package com.google.gerrit.server.change; +import com.google.common.base.Strings; import com.google.gerrit.common.FooterConstants; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.reviewdb.client.Branch; @@ -182,9 +183,13 @@ } else { // Change key not found on destination branch. We can create a new // change. + String newTopic = null; + if (!Strings.isNullOrEmpty(change.getTopic())) { + newTopic = change.getTopic() + "-" + newDest.getShortName(); + } Change newChange = createNewChange(git, revWalk, changeKey, project, destRef, cherryPickCommit, refControl, - identifiedUser, change.getTopic()); + identifiedUser, newTopic); addMessageToSourceChange(change, patch.getId(), destinationBranch, cherryPickCommit, identifiedUser, refControl);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java index 4a52fc5..b155b84 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
@@ -14,9 +14,11 @@ package com.google.gerrit.server.change; -import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.gerrit.server.PatchLineCommentsUtil.COMMENT_INFO_ORDER; +import com.google.common.base.Function; import com.google.common.base.Strings; +import com.google.common.collect.FluentIterable; import com.google.gerrit.extensions.client.Comment.Range; import com.google.gerrit.extensions.client.Side; import com.google.gerrit.extensions.common.CommentInfo; @@ -26,45 +28,51 @@ import com.google.gerrit.server.account.AccountLoader; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; -import com.google.inject.Singleton; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; -@Singleton class CommentJson { private final AccountLoader.Factory accountLoaderFactory; + private boolean fillAccounts = true; + private boolean fillPatchSet; + @Inject CommentJson(AccountLoader.Factory accountLoaderFactory) { this.accountLoaderFactory = accountLoaderFactory; } - CommentInfo format(PatchLineComment c) throws OrmException { - return format(c, true); + CommentJson setFillAccounts(boolean fillAccounts) { + this.fillAccounts = fillAccounts; + return this; } - CommentInfo format(PatchLineComment c, boolean fill) throws OrmException { + CommentJson setFillPatchSet(boolean fillPatchSet) { + this.fillPatchSet = fillPatchSet; + return this; + } + + CommentInfo format(PatchLineComment c) throws OrmException { AccountLoader loader = null; - if (fill) { + if (fillAccounts) { loader = accountLoaderFactory.create(true); } CommentInfo commentInfo = toCommentInfo(c, loader); - if (fill) { + if (fillAccounts) { loader.fill(); } return commentInfo; } - Map<String, List<CommentInfo>> format(Iterable<PatchLineComment> l, - boolean fill) throws OrmException { + Map<String, List<CommentInfo>> format(Iterable<PatchLineComment> l) + throws OrmException { Map<String, List<CommentInfo>> out = new TreeMap<>(); - AccountLoader accountLoader = fill + AccountLoader accountLoader = fillAccounts ? accountLoaderFactory.create(true) : null; @@ -80,20 +88,7 @@ } for (List<CommentInfo> list : out.values()) { - Collections.sort(list, new Comparator<CommentInfo>() { - @Override - public int compare(CommentInfo a, CommentInfo b) { - int c = firstNonNull(a.side, Side.REVISION).ordinal() - - firstNonNull(b.side, Side.REVISION).ordinal(); - if (c == 0) { - c = firstNonNull(a.line, 0) - firstNonNull(b.line, 0); - } - if (c == 0) { - c = a.id.compareTo(b.id); - } - return c; - } - }); + Collections.sort(list, COMMENT_INFO_ORDER); } if (accountLoader != null) { @@ -103,8 +98,32 @@ return out; } + List<CommentInfo> formatAsList(Iterable<PatchLineComment> l) + throws OrmException { + final AccountLoader accountLoader = fillAccounts + ? accountLoaderFactory.create(true) + : null; + List<CommentInfo> out = FluentIterable + .from(l) + .transform(new Function<PatchLineComment, CommentInfo>() { + @Override + public CommentInfo apply(PatchLineComment c) { + return toCommentInfo(c, accountLoader); + } + }).toSortedList(COMMENT_INFO_ORDER); + + if (accountLoader != null) { + accountLoader.fill(); + } + + return out; + } + private CommentInfo toCommentInfo(PatchLineComment c, AccountLoader loader) { CommentInfo r = new CommentInfo(); + if (fillPatchSet) { + r.patchSet = c.getKey().getParentKey().getParentKey().get(); + } r.id = Url.encode(c.getKey().get()); r.path = c.getKey().getParentKey().getFileName(); if (c.getSide() == 0) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java index eff408e..8f78f0e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
@@ -31,13 +31,13 @@ @Singleton public class Comments implements ChildCollection<RevisionResource, CommentResource> { private final DynamicMap<RestView<CommentResource>> views; - private final ListComments list; + private final ListRevisionComments list; private final Provider<ReviewDb> dbProvider; private final PatchLineCommentsUtil plcUtil; @Inject Comments(DynamicMap<RestView<CommentResource>> views, - ListComments list, Provider<ReviewDb> dbProvider, + ListRevisionComments list, Provider<ReviewDb> dbProvider, PatchLineCommentsUtil plcUtil) { this.views = views; this.list = list; @@ -51,7 +51,7 @@ } @Override - public ListComments list() { + public ListRevisionComments list() { return list; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java index ee28ed2..87a1ca6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -14,13 +14,14 @@ package com.google.gerrit.server.change; +import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER; +import static com.google.gerrit.server.ChangeUtil.TO_PS_ID; + import com.google.auto.value.AutoValue; -import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.Ordering; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.api.changes.FixInput; import com.google.gerrit.extensions.common.ProblemInfo; @@ -210,17 +211,6 @@ } } - private static final Function<PatchSet, Integer> TO_PS_ID = - new Function<PatchSet, Integer>() { - @Override - public Integer apply(PatchSet in) { - return in.getId().get(); - } - }; - - private static final Ordering<PatchSet> PS_ID_ORDER = Ordering.natural() - .onResultOf(TO_PS_ID); - private boolean checkPatchSets() { List<PatchSet> all; try { @@ -307,8 +297,7 @@ return; } if (dest == null) { - problem("Destination ref not found (may be new branch): " - + change.getDest().get()); + problem("Destination ref not found (may be new branch): " + refName); return; } RevCommit tip = parseCommit(dest.getObjectId(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java index 4dffd67..7320c7a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -33,6 +33,7 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.CurrentUser; @@ -55,7 +56,6 @@ import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.PersonIdent; @@ -140,11 +140,7 @@ } } - String refName = input.branch; - if (!refName.startsWith(Constants.R_REFS)) { - refName = Constants.R_HEADS + input.branch; - } - + String refName = RefNames.fullName(input.branch); ProjectResource rsrc = projectsCollection.parse(input.project); Capable r = rsrc.getControl().canPushToAtLeastOneRef();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java index 36b9692..a503721 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java
@@ -45,14 +45,14 @@ public class CreateDraftComment implements RestModifyView<RevisionResource, DraftInput> { private final Provider<ReviewDb> db; private final ChangeUpdate.Factory updateFactory; - private final CommentJson commentJson; + private final Provider<CommentJson> commentJson; private final PatchLineCommentsUtil plcUtil; private final PatchListCache patchListCache; @Inject CreateDraftComment(Provider<ReviewDb> db, ChangeUpdate.Factory updateFactory, - CommentJson commentJson, + Provider<CommentJson> commentJson, PatchLineCommentsUtil plcUtil, PatchListCache patchListCache) { this.db = db; @@ -93,6 +93,6 @@ setCommentRevId(c, patchListCache, rsrc.getChange(), rsrc.getPatchSet()); plcUtil.insertComments(db.get(), update, Collections.singleton(c)); update.commit(); - return Response.created(commentJson.format(c, false)); + return Response.created(commentJson.get().setFillAccounts(false).format(c)); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java index 7edd679..acb50ac 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java
@@ -33,14 +33,14 @@ public class DraftComments implements ChildCollection<RevisionResource, DraftCommentResource> { private final DynamicMap<RestView<DraftCommentResource>> views; private final Provider<CurrentUser> user; - private final ListDraftComments list; + private final ListRevisionDrafts list; private final Provider<ReviewDb> dbProvider; private final PatchLineCommentsUtil plcUtil; @Inject DraftComments(DynamicMap<RestView<DraftCommentResource>> views, Provider<CurrentUser> user, - ListDraftComments list, + ListRevisionDrafts list, Provider<ReviewDb> dbProvider, PatchLineCommentsUtil plcUtil) { this.views = views; @@ -56,7 +56,7 @@ } @Override - public ListDraftComments list() throws AuthException { + public ListRevisionDrafts list() throws AuthException { checkIdentifiedUser(); return list; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java index 6330e34..122156b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -14,7 +14,8 @@ package com.google.gerrit.server.change; -import com.google.common.collect.Lists; +import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER; + import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; @@ -24,7 +25,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.git.EmailReviewCommentsExecutor; -import com.google.gerrit.server.git.WorkQueue.Executor; import com.google.gerrit.server.mail.CommentSender; import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.util.RequestContext; @@ -40,9 +40,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; -import java.util.Comparator; import java.util.List; +import java.util.concurrent.ExecutorService; public class EmailReviewComments implements Runnable, RequestContext { private static final Logger log = LoggerFactory.getLogger(EmailReviewComments.class); @@ -57,7 +56,7 @@ List<PatchLineComment> comments); } - private final Executor sendEmailsExecutor; + private final ExecutorService sendEmailsExecutor; private final PatchSetInfoFactory patchSetInfoFactory; private final CommentSender.Factory commentSenderFactory; private final SchemaFactory<ReviewDb> schemaFactory; @@ -73,7 +72,7 @@ @Inject EmailReviewComments ( - @EmailReviewCommentsExecutor final Executor executor, + @EmailReviewCommentsExecutor ExecutorService executor, PatchSetInfoFactory patchSetInfoFactory, CommentSender.Factory commentSenderFactory, SchemaFactory<ReviewDb> schemaFactory, @@ -94,7 +93,7 @@ this.patchSet = patchSet; this.authorId = authorId; this.message = message; - this.comments = comments; + this.comments = PLC_ORDER.sortedCopy(comments); } void sendAsync() { @@ -103,33 +102,10 @@ @Override public void run() { + RequestContext old = requestContext.setContext(this); try { - requestContext.setContext(this); - comments = Lists.newArrayList(comments); - Collections.sort(comments, new Comparator<PatchLineComment>() { - @Override - public int compare(PatchLineComment a, PatchLineComment b) { - int cmp = path(a).compareTo(path(b)); - if (cmp != 0) { - return cmp; - } - - // 0 is ancestor, 1 is revision. Sort ancestor first. - cmp = a.getSide() - b.getSide(); - if (cmp != 0) { - return cmp; - } - - return a.getLine() - b.getLine(); - } - - private String path(PatchLineComment c) { - return c.getKey().getParentKey().getFileName(); - } - }); - - CommentSender cm = commentSenderFactory.create(notify, change); + CommentSender cm = commentSenderFactory.create(notify, change.getId()); cm.setFrom(authorId); cm.setPatchSet(patchSet, patchSetInfoFactory.get(change, patchSet)); cm.setChangeMessage(message); @@ -138,7 +114,7 @@ } catch (Exception e) { log.error("Cannot email comments for " + patchSet.getId(), e); } finally { - requestContext.setContext(null); + requestContext.setContext(old); if (db != null) { db.close(); db = null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileResource.java index 1662237..ca47fb9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileResource.java
@@ -44,7 +44,7 @@ return rev.getAccountId(); } - RevisionResource getRevision() { + public RevisionResource getRevision() { return rev; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java index 913f69e..ccc7645 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetArchive.java
@@ -18,16 +18,15 @@ import com.google.common.collect.ImmutableMap; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BinaryResult; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.config.ConfigUtil; -import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.DownloadConfig; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.jgit.api.ArchiveCommand; import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; @@ -36,11 +35,7 @@ import java.io.IOException; import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -51,33 +46,25 @@ final Set<ArchiveFormat> allowed; @Inject - AllowedFormats(@GerritServerConfig Config cfg) { - Collection<ArchiveFormat> enabled; - String v = cfg.getString("download", null, "archive"); - if (v == null) { - enabled = Arrays.asList(ArchiveFormat.values()); - } else if (v.isEmpty() || "off".equalsIgnoreCase(v)) { - enabled = Collections.emptyList(); - } else { - enabled = ConfigUtil.getEnumList(cfg, - "download", null, "archive", - ArchiveFormat.TGZ); - } - + AllowedFormats(DownloadConfig cfg) { Map<String, ArchiveFormat> exts = new HashMap<>(); - for (ArchiveFormat format : enabled) { + for (ArchiveFormat format : cfg.getArchiveFormats()) { for (String ext : format.getSuffixes()) { exts.put(ext, format); } exts.put(format.name().toLowerCase(), format); } extensions = ImmutableMap.copyOf(exts); - allowed = Collections.unmodifiableSet(new LinkedHashSet<>(enabled)); + allowed = cfg.getArchiveFormats(); } public Set<ArchiveFormat> getAllowed() { return allowed; } + + public ImmutableMap<String, ArchiveFormat> getExtensions() { + return extensions; + } } private final GitRepositoryManager repoManager; @@ -93,8 +80,8 @@ } @Override - public BinaryResult apply(RevisionResource rsrc) - throws BadRequestException, IOException { + public BinaryResult apply(RevisionResource rsrc) throws BadRequestException, + IOException, MethodNotAllowedException { if (Strings.isNullOrEmpty(format)) { throw new BadRequestException("format is not specified"); } @@ -102,6 +89,9 @@ if (f == null) { throw new BadRequestException("unknown archive format"); } + if (f == ArchiveFormat.ZIP) { + throw new MethodNotAllowedException("zip format is disabled"); + } boolean close = true; final Repository repo = repoManager .openRepository(rsrc.getControl().getProject().getNameKey());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java index ea84f50..d87c7eb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
@@ -18,20 +18,21 @@ import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; @Singleton public class GetComment implements RestReadView<CommentResource> { - private final CommentJson commentJson; + private final Provider<CommentJson> commentJson; @Inject - GetComment(CommentJson commentJson) { + GetComment(Provider<CommentJson> commentJson) { this.commentJson = commentJson; } @Override public CommentInfo apply(CommentResource rsrc) throws OrmException { - return commentJson.format(rsrc.getComment()); + return commentJson.get().format(rsrc.getComment()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java index 296a262..0f0a6a2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetCommit.java
@@ -18,38 +18,47 @@ import com.google.gerrit.extensions.restapi.CacheControl; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; -import com.google.gwtorm.server.OrmException; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.git.GitRepositoryManager; import com.google.inject.Inject; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.kohsuke.args4j.Option; +import java.io.IOException; import java.util.concurrent.TimeUnit; public class GetCommit implements RestReadView<RevisionResource> { + private final GitRepositoryManager repoManager; private final ChangeJson json; @Option(name = "--links", usage = "Add weblinks") private boolean addLinks; @Inject - GetCommit(ChangeJson json) { + GetCommit(GitRepositoryManager repoManager, + ChangeJson json) { + this.repoManager = repoManager; this.json = json; } @Override - public Response<CommitInfo> apply(RevisionResource resource) - throws OrmException { - try { - Response<CommitInfo> r = - Response.ok(json.toCommit(resource.getPatchSet(), resource - .getChange().getProject(), addLinks)); - if (resource.isCacheable()) { + public Response<CommitInfo> apply(RevisionResource rsrc) throws IOException { + Project.NameKey p = rsrc.getChange().getProject(); + try (Repository repo = repoManager.openRepository(p); + RevWalk rw = new RevWalk(repo)) { + String rev = rsrc.getPatchSet().getRevision().get(); + RevCommit commit = rw.parseCommit(ObjectId.fromString(rev)); + rw.parseBody(commit); + Response<CommitInfo> r = Response.ok( + json.toCommit(rsrc.getControl(), rw, commit, addLinks)); + if (rsrc.isCacheable()) { r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS)); } return r; - } catch (PatchSetInfoNotAvailableException e) { - throw new OrmException(e); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java index 8e3a5d1..eef0533 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
@@ -201,6 +201,7 @@ result.metaA.lines = ps.getA().size(); result.metaA.webLinks = getFileWebLinks(state.getProject(), revA, result.metaA.name); + result.metaA.commitId = content.commitIdA; } if (ps.getDisplayMethodB() != DisplayMethod.NONE) { @@ -211,6 +212,7 @@ result.metaB.lines = ps.getB().size(); result.metaB.webLinks = getFileWebLinks(state.getProject(), revB, result.metaB.name); + result.metaB.commitId = content.commitIdB; } if (intraline) { @@ -264,6 +266,8 @@ final SparseFileContent fileA; final SparseFileContent fileB; final boolean ignoreWS; + final String commitIdA; + final String commitIdB; int nextA; int nextB; @@ -273,6 +277,8 @@ fileA = ps.getA(); fileB = ps.getB(); ignoreWS = ps.isIgnoreWhitespace(); + commitIdA = ps.getCommitIdA(); + commitIdB = ps.getCommitIdB(); } void addCommon(int end) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraftComment.java index a13ecdf..22f90c9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraftComment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraftComment.java
@@ -18,20 +18,21 @@ import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; @Singleton public class GetDraftComment implements RestReadView<DraftCommentResource> { - private final CommentJson commentJson; + private final Provider<CommentJson> commentJson; @Inject - GetDraftComment(CommentJson commentJson) { + GetDraftComment(Provider<CommentJson> commentJson) { this.commentJson = commentJson; } @Override public CommentInfo apply(DraftCommentResource rsrc) throws OrmException { - return commentJson.format(rsrc.getComment()); + return commentJson.get().format(rsrc.getComment()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetHashtags.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetHashtags.java index 4846c0b..d0c1e83 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetHashtags.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetHashtags.java
@@ -30,9 +30,8 @@ @Singleton public class GetHashtags implements RestReadView<ChangeResource> { @Override - public Response<? extends Set<String>> apply(ChangeResource req) + public Response<Set<String>> apply(ChangeResource req) throws AuthException, OrmException, IOException, BadRequestException { - ChangeControl control = req.getControl(); ChangeNotes notes = control.getNotes().load(); Set<String> hashtags = notes.getHashtags();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java index 6cdae44..7f49192 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
@@ -144,7 +144,7 @@ Map<PatchSet.Id, PatchSet> r = Maps.newHashMapWithExpectedSize(cds.size() * 2); for (ChangeData cd : cds) { - for (PatchSet p : cd.patches()) { + for (PatchSet p : cd.patchSets()) { r.put(p.getId(), p); } } @@ -277,6 +277,9 @@ public Integer _revisionNumber; public Integer _currentRevisionNumber; + public ChangeAndCommit() { + } + ChangeAndCommit(@Nullable Change change, @Nullable PatchSet ps, RevCommit c) { if (change != null) { changeId = change.getKey().get();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java index d58c8d2..3ae6f6c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java
@@ -14,22 +14,65 @@ package com.google.gerrit.server.change; +import com.google.common.base.Strings; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.gerrit.extensions.common.ActionInfo; +import com.google.gerrit.extensions.restapi.ETagView; import com.google.gerrit.extensions.restapi.Response; -import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gerrit.server.query.change.InternalChangeQuery; +import com.google.gwtorm.server.OrmException; +import com.google.gwtorm.server.OrmRuntimeException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; -@Singleton -public class GetRevisionActions implements RestReadView<RevisionResource> { - private final ActionJson delegate; +import org.eclipse.jgit.lib.Config; +import java.util.Map; + +@Singleton +public class GetRevisionActions implements ETagView<RevisionResource> { + private final ActionJson delegate; + private final Provider<InternalChangeQuery> queryProvider; + private final Config config; + private final RebaseChange rebaseChange; @Inject - GetRevisionActions(ActionJson delegate) { + GetRevisionActions( + ActionJson delegate, + Provider<InternalChangeQuery> queryProvider, + @GerritServerConfig Config config, + RebaseChange rebaseChange) { this.delegate = delegate; + this.queryProvider = queryProvider; + this.config = config; + this.rebaseChange = rebaseChange; } @Override - public Object apply(RevisionResource rsrc) { + public Response<Map<String, ActionInfo>> apply(RevisionResource rsrc) { return Response.withMustRevalidate(delegate.format(rsrc)); } + + @Override + public String getETag(RevisionResource rsrc) { + String topic = rsrc.getChange().getTopic(); + if (!Submit.wholeTopicEnabled(config) + || Strings.isNullOrEmpty(topic)) { + return rsrc.getETag(); + } + Hasher h = Hashing.md5().newHasher(); + CurrentUser user = rsrc.getControl().getCurrentUser(); + try { + for (ChangeData c : queryProvider.get().byTopicOpen(topic)) { + new ChangeResource(c.changeControl(), rebaseChange).prepareETag(h, user); + } + } catch (OrmException e){ + throw new OrmRuntimeException(e); + } + return h.hash().toString(); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java new file mode 100644 index 0000000..97befa0 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java
@@ -0,0 +1,58 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.common.CommentInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.PatchLineCommentsUtil; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import java.util.List; +import java.util.Map; + +@Singleton +public class ListChangeComments implements RestReadView<ChangeResource> { + private final Provider<ReviewDb> db; + private final ChangeData.Factory changeDataFactory; + private final Provider<CommentJson> commentJson; + private final PatchLineCommentsUtil plcUtil; + + @Inject + ListChangeComments(Provider<ReviewDb> db, + ChangeData.Factory changeDataFactory, + Provider<CommentJson> commentJson, + PatchLineCommentsUtil plcUtil) { + this.db = db; + this.changeDataFactory = changeDataFactory; + this.commentJson = commentJson; + this.plcUtil = plcUtil; + } + + @Override + public Map<String, List<CommentInfo>> apply( + ChangeResource rsrc) throws AuthException, OrmException { + ChangeData cd = changeDataFactory.create(db.get(), rsrc.getControl()); + return commentJson.get() + .setFillAccounts(true) + .setFillPatchSet(true) + .format(plcUtil.publishedByChange(db.get(), cd.notes())); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java new file mode 100644 index 0000000..2b5d7d9 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java
@@ -0,0 +1,66 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.common.CommentInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.PatchLineCommentsUtil; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import java.util.List; +import java.util.Map; + +@Singleton +public class ListChangeDrafts implements RestReadView<ChangeResource> { + private final Provider<ReviewDb> db; + private final ChangeData.Factory changeDataFactory; + private final Provider<CommentJson> commentJson; + private final PatchLineCommentsUtil plcUtil; + + @Inject + ListChangeDrafts(Provider<ReviewDb> db, + ChangeData.Factory changeDataFactory, + Provider<CommentJson> commentJson, + PatchLineCommentsUtil plcUtil) { + this.db = db; + this.changeDataFactory = changeDataFactory; + this.commentJson = commentJson; + this.plcUtil = plcUtil; + } + + @Override + public Map<String, List<CommentInfo>> apply( + ChangeResource rsrc) throws AuthException, OrmException { + if (!rsrc.getControl().getCurrentUser().isIdentifiedUser()) { + throw new AuthException("Authentication required"); + } + IdentifiedUser user = (IdentifiedUser) rsrc.getControl().getCurrentUser(); + ChangeData cd = changeDataFactory.create(db.get(), rsrc.getControl()); + List<PatchLineComment> drafts = + plcUtil.draftByChangeAuthor(db.get(), cd.notes(), user.getAccountId()); + return commentJson.get() + .setFillAccounts(false) + .setFillPatchSet(true) + .format(drafts); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java similarity index 90% rename from gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java rename to gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java index b50e243..2392781 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java
@@ -24,10 +24,10 @@ import com.google.inject.Singleton; @Singleton -public class ListComments extends ListDraftComments { +public class ListRevisionComments extends ListRevisionDrafts { @Inject - ListComments(Provider<ReviewDb> db, - CommentJson commentJson, + ListRevisionComments(Provider<ReviewDb> db, + Provider<CommentJson> commentJson, PatchLineCommentsUtil plcUtil) { super(db, commentJson, plcUtil); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDraftComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java similarity index 76% rename from gerrit-server/src/main/java/com/google/gerrit/server/change/ListDraftComments.java rename to gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java index 3375cba..ef12b2a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDraftComments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
@@ -28,14 +28,14 @@ import java.util.Map; @Singleton -public class ListDraftComments implements RestReadView<RevisionResource> { +public class ListRevisionDrafts implements RestReadView<RevisionResource> { protected final Provider<ReviewDb> db; - protected CommentJson commentJson; + protected final Provider<CommentJson> commentJson; protected final PatchLineCommentsUtil plcUtil; @Inject - ListDraftComments(Provider<ReviewDb> db, - CommentJson commentJson, + ListRevisionDrafts(Provider<ReviewDb> db, + Provider<CommentJson> commentJson, PatchLineCommentsUtil plcUtil) { this.db = db; this.commentJson = commentJson; @@ -55,6 +55,15 @@ @Override public Map<String, List<CommentInfo>> apply(RevisionResource rsrc) throws OrmException { - return commentJson.format(listComments(rsrc), includeAuthorInfo()); + return commentJson.get() + .setFillAccounts(includeAuthorInfo()) + .format(listComments(rsrc)); + } + + public List<CommentInfo> getComments(RevisionResource rsrc) + throws OrmException { + return commentJson.get() + .setFillAccounts(includeAuthorInfo()) + .formatAsList(listComments(rsrc)); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java index d0e4c99..b73af931 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -52,6 +52,8 @@ get(CHANGE_KIND, "topic").to(GetTopic.class); get(CHANGE_KIND, "in").to(IncludedIn.class); get(CHANGE_KIND, "hashtags").to(GetHashtags.class); + get(CHANGE_KIND, "comments").to(ListChangeComments.class); + get(CHANGE_KIND, "drafts").to(ListChangeDrafts.class); get(CHANGE_KIND, "check").to(Check.class); post(CHANGE_KIND, "check").to(Check.class); put(CHANGE_KIND, "topic").to(PutTopic.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java index baddd40..6716162 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -293,7 +293,7 @@ try { PatchSetInfo info = patchSetInfoFactory.get(commit, patchSet.getId()); ReplacePatchSetSender cm = - replacePatchSetFactory.create(updatedChange); + replacePatchSetFactory.create(c.getId()); cm.setFrom(user.getAccountId()); cm.setPatchSet(patchSet, info); cm.setChangeMessage(changeMessage);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostHashtags.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostHashtags.java index 6638f91..62520f4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostHashtags.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostHashtags.java
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.server.validators.ValidationException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; @@ -29,7 +30,8 @@ import java.util.Set; @Singleton -public class PostHashtags implements RestModifyView<ChangeResource, HashtagsInput> { +public class PostHashtags implements RestModifyView<ChangeResource, HashtagsInput>, + UiAction<ChangeResource>{ private HashtagsUtil hashtagsUtil; @Inject @@ -38,12 +40,12 @@ } @Override - public Response<? extends Set<String>> apply(ChangeResource req, HashtagsInput input) + public Response<Set<String>> apply(ChangeResource req, HashtagsInput input) throws AuthException, OrmException, IOException, BadRequestException, ResourceConflictException { try { - return Response.ok(hashtagsUtil.setHashtags( + return Response.<Set<String>> ok(hashtagsUtil.setHashtags( req.getControl(), input, true, true)); } catch (IllegalArgumentException e) { throw new BadRequestException(e.getMessage()); @@ -51,4 +53,11 @@ throw new ResourceConflictException(e.getMessage()); } } -} + + @Override + public UiAction.Description getDescription(ChangeResource resource) { + return new UiAction.Description() + .setLabel("Edit Hashtags") + .setVisible(resource.getControl().canEditHashtags()); + } +} \ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java index 8e989b6..e10077a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -22,8 +22,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.data.LabelType; @@ -180,11 +178,8 @@ db.get().rollback(); } - CheckedFuture<?, IOException> indexWrite; if (dirty) { - indexWrite = indexer.indexAsync(change.getId()); - } else { - indexWrite = Futures.<Void, IOException> immediateCheckedFuture(null); + indexer.index(db.get(), change); } if (message != null && input.notify.compareTo(NotifyHandling.NONE) > 0) { email.create( @@ -198,7 +193,6 @@ Output output = new Output(); output.labels = input.labels; - indexWrite.checkedGet(); if (message != null) { fireCommentAddedHook(revision); } @@ -351,7 +345,11 @@ Map<String, PatchLineComment> drafts = Collections.emptyMap(); if (!in.isEmpty() || draftsHandling != DraftHandling.KEEP) { - drafts = scanDraftComments(rsrc); + if (draftsHandling == DraftHandling.PUBLISH_ALL_REVISIONS) { + drafts = changeDrafts(rsrc); + } else { + drafts = patchSetDrafts(rsrc); + } } List<PatchLineComment> del = Lists.newArrayList(); @@ -376,8 +374,7 @@ e.setStatus(PatchLineComment.Status.PUBLISHED); e.setWrittenOn(timestamp); e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1); - setCommentRevId(e, patchListCache, rsrc.getChange(), - rsrc.getPatchSet()); + setCommentRevId(e, patchListCache, rsrc.getChange(), rsrc.getPatchSet()); e.setMessage(c.message); if (c.range != null) { e.setRange(new CommentRange( @@ -399,11 +396,11 @@ del.addAll(drafts.values()); break; case PUBLISH: + case PUBLISH_ALL_REVISIONS: for (PatchLineComment e : drafts.values()) { e.setStatus(PatchLineComment.Status.PUBLISHED); e.setWrittenOn(timestamp); - setCommentRevId(e, patchListCache, rsrc.getChange(), - rsrc.getPatchSet()); + setCommentRevId(e, patchListCache, rsrc.getChange(), rsrc.getPatchSet()); ups.add(e); } break; @@ -414,8 +411,18 @@ return !del.isEmpty() || !ups.isEmpty(); } - private Map<String, PatchLineComment> scanDraftComments( - RevisionResource rsrc) throws OrmException { + private Map<String, PatchLineComment> changeDrafts(RevisionResource rsrc) + throws OrmException { + Map<String, PatchLineComment> drafts = Maps.newHashMap(); + for (PatchLineComment c : plcUtil.draftByChangeAuthor( + db.get(), rsrc.getNotes(), rsrc.getAccountId())) { + drafts.put(c.getKey().get(), c); + } + return drafts; + } + + private Map<String, PatchLineComment> patchSetDrafts(RevisionResource rsrc) + throws OrmException { Map<String, PatchLineComment> drafts = Maps.newHashMap(); for (PatchLineComment c : plcUtil.draftByPatchSetAuthor(db.get(), rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes())) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java index ecfb43c..fc27010 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -255,8 +255,8 @@ ImmutableList.of(psa))); } accountLoaderFactory.create(true).fill(result.reviewers); - emailReviewers(rsrc.getChange(), added); indexFuture.checkedGet(); + emailReviewers(rsrc.getChange(), added); if (!added.isEmpty()) { PatchSet patchSet = dbProvider.get().patchSets().get(rsrc.getChange().currentPatchSetId()); for (PatchSetApproval psa : added) { @@ -283,7 +283,7 @@ } if (!toMail.isEmpty()) { try { - AddReviewerSender cm = addReviewerSenderFactory.create(change); + AddReviewerSender cm = addReviewerSenderFactory.create(change.getId()); cm.setFrom(identifiedUser.getAccountId()); cm.addReviewers(toMail); cm.send();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java index 9f77f0e..dd9a44d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java
@@ -14,7 +14,6 @@ package com.google.gerrit.server.change; -import com.google.common.util.concurrent.CheckedFuture; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ResourceConflictException; @@ -82,13 +81,11 @@ if (!updatedPatchSet.isDraft() || updatedChange.getStatus() == Change.Status.NEW) { - CheckedFuture<?, IOException> indexFuture = - indexer.indexAsync(updatedChange.getId()); + indexer.index(dbProvider.get(), updatedChange); sender.send(rsrc.getNotes(), update, rsrc.getChange().getStatus() == Change.Status.DRAFT, rsrc.getUser(), updatedChange, updatedPatchSet, rsrc.getControl().getLabelTypes()); - indexFuture.checkedGet(); hooks.doDraftPublishedHook(updatedChange, updatedPatchSet, dbProvider.get()); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java index 2a0bcb3..a4a5e16 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
@@ -45,7 +45,7 @@ private final DeleteDraftComment delete; private final PatchLineCommentsUtil plcUtil; private final ChangeUpdate.Factory updateFactory; - private final CommentJson commentJson; + private final Provider<CommentJson> commentJson; private final PatchListCache patchListCache; @Inject @@ -53,7 +53,7 @@ DeleteDraftComment delete, PatchLineCommentsUtil plcUtil, ChangeUpdate.Factory updateFactory, - CommentJson commentJson, + Provider<CommentJson> commentJson, PatchListCache patchListCache) { this.db = db; this.delete = delete; @@ -96,14 +96,13 @@ Collections.singleton(update(c, in))); } else { if (c.getRevId() == null) { - setCommentRevId(c, patchListCache, rsrc.getChange(), - rsrc.getPatchSet()); + setCommentRevId(c, patchListCache, rsrc.getChange(), rsrc.getPatchSet()); } plcUtil.updateComments(db.get(), update, Collections.singleton(update(c, in))); } update.commit(); - return Response.ok(commentJson.format(c, false)); + return Response.ok(commentJson.get().setFillAccounts(false).format(c)); } private PatchLineComment update(PatchLineComment e, DraftInput in) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java index d2afd9a3..5aacef7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
@@ -14,6 +14,7 @@ package com.google.gerrit.server.change; +import com.google.common.primitives.Ints; import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.extensions.api.changes.RebaseInput; import com.google.gerrit.extensions.client.ListChangesOption; @@ -26,10 +27,10 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change.Status; import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.client.PatchSetAncestor; +import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.changedetail.RebaseChange; +import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.NoSuchChangeException; @@ -38,27 +39,32 @@ import com.google.inject.Provider; import com.google.inject.Singleton; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; - @Singleton public class Rebase implements RestModifyView<RevisionResource, RebaseInput>, UiAction<RevisionResource> { - private static final Logger log = - LoggerFactory.getLogger(Rebase.class); + private static final Logger log = LoggerFactory.getLogger(Rebase.class); + private final GitRepositoryManager repoManager; private final Provider<RebaseChange> rebaseChange; private final ChangeJson json; private final Provider<ReviewDb> dbProvider; @Inject - public Rebase(Provider<RebaseChange> rebaseChange, ChangeJson json, + public Rebase(GitRepositoryManager repoManager, + Provider<RebaseChange> rebaseChange, + ChangeJson json, Provider<ReviewDb> dbProvider) { + this.repoManager = repoManager; this.rebaseChange = rebaseChange; this.json = json .addOption(ListChangesOption.CURRENT_REVISION) @@ -69,68 +75,24 @@ @Override public ChangeInfo apply(RevisionResource rsrc, RebaseInput input) throws AuthException, ResourceNotFoundException, - ResourceConflictException, EmailException, OrmException { + ResourceConflictException, EmailException, OrmException, IOException { ChangeControl control = rsrc.getControl(); Change change = rsrc.getChange(); - if (!control.canRebase()) { - throw new AuthException("rebase not permitted"); - } else if (!change.getStatus().isOpen()) { - throw new ResourceConflictException("change is " - + change.getStatus().name().toLowerCase()); - } else if (!hasOneParent(rsrc.getPatchSet().getId())) { - throw new ResourceConflictException( - "cannot rebase merge commits or commit with no ancestor"); - } - - String baseRev = null; - if (input != null && input.base != null) { - String base = input.base.trim(); - do { - if (base.equals("")) { - // remove existing dependency to other patch set - baseRev = change.getDest().get(); - break; - } - - ReviewDb db = dbProvider.get(); - PatchSet basePatchSet = parseBase(base); - if (basePatchSet == null) { - throw new ResourceConflictException("base revision is missing: " + base); - } else if (!rsrc.getControl().isPatchVisible(basePatchSet, db)) { - throw new AuthException("base revision not accessible: " + base); - } else if (change.getId().equals(basePatchSet.getId().getParentKey())) { - throw new ResourceConflictException("cannot depend on self"); - } - - Change baseChange = db.changes().get(basePatchSet.getId().getParentKey()); - if (baseChange != null) { - if (!baseChange.getProject().equals(change.getProject())) { - throw new ResourceConflictException("base change is in wrong project: " - + baseChange.getProject()); - } else if (!baseChange.getDest().equals(change.getDest())) { - throw new ResourceConflictException("base change is targetting wrong branch: " - + baseChange.getDest()); - } else if (baseChange.getStatus() == Status.ABANDONED) { - throw new ResourceConflictException("base change is abandoned: " - + baseChange.getKey()); - } else if (isDescendantOf(baseChange.getId(), rsrc.getPatchSet().getRevision())) { - throw new ResourceConflictException("base change " + baseChange.getKey() - + " is a descendant of the current " - + " change - recursion not allowed"); - } - baseRev = basePatchSet.getRevision().get(); - break; - } - } while (false); // just wanted to use the break statement - } - - try { - rebaseChange.get().rebase(change, rsrc.getPatchSet().getId(), - rsrc.getUser(), baseRev); + try (Repository repo = repoManager.openRepository(change.getProject()); + RevWalk rw = new RevWalk(repo)) { + if (!control.canRebase()) { + throw new AuthException("rebase not permitted"); + } else if (!change.getStatus().isOpen()) { + throw new ResourceConflictException("change is " + + change.getStatus().name().toLowerCase()); + } else if (!hasOneParent(rw, rsrc.getPatchSet())) { + throw new ResourceConflictException( + "cannot rebase merge commits or commit with no ancestor"); + } + rebaseChange.get().rebase(repo, rw, change, rsrc.getPatchSet().getId(), + rsrc.getUser(), findBaseRev(rw, rsrc, input)); } catch (InvalidChangeOperationException e) { throw new ResourceConflictException(e.getMessage()); - } catch (IOException e) { - throw new ResourceConflictException(e.getMessage()); } catch (NoSuchChangeException e) { throw new ResourceNotFoundException(change.getId().toString()); } @@ -138,88 +100,118 @@ return json.format(change.getId()); } - private boolean isDescendantOf(Change.Id child, RevId ancestor) - throws OrmException { - ReviewDb db = dbProvider.get(); - - ArrayList<RevId> parents = new ArrayList<>(); - parents.add(ancestor); - while (!parents.isEmpty()) { - RevId parent = parents.remove(0); - // get direct descendants of change - for (PatchSetAncestor desc : db.patchSetAncestors().descendantsOf(parent)) { - PatchSet descPatchSet = db.patchSets().get(desc.getPatchSet()); - Change.Id descChangeId = descPatchSet.getId().getParentKey(); - if (child.equals(descChangeId)) { - PatchSet.Id descCurrentPatchSetId = - db.changes().get(descChangeId).currentPatchSetId(); - // it's only bad if the descendant patch set is current - return descPatchSet.getId().equals(descCurrentPatchSetId); - } else { - // process indirect descendants as well - parents.add(descPatchSet.getRevision()); - } - } + private String findBaseRev(RevWalk rw, RevisionResource rsrc, + RebaseInput input) throws AuthException, ResourceConflictException, + OrmException, IOException { + if (input == null || input.base == null) { + return null; } - return false; + Change change = rsrc.getChange(); + String base = input.base.trim(); + if (base.equals("")) { + // remove existing dependency to other patch set + return change.getDest().get(); + } + + ReviewDb db = dbProvider.get(); + PatchSet basePatchSet = parseBase(base); + if (basePatchSet == null) { + throw new ResourceConflictException("base revision is missing: " + base); + } else if (!rsrc.getControl().isPatchVisible(basePatchSet, db)) { + throw new AuthException("base revision not accessible: " + base); + } else if (change.getId().equals(basePatchSet.getId().getParentKey())) { + throw new ResourceConflictException("cannot depend on self"); + } + + Change baseChange = db.changes().get(basePatchSet.getId().getParentKey()); + if (baseChange == null) { + return null; + } + if (!baseChange.getProject().equals(change.getProject())) { + throw new ResourceConflictException( + "base change is in wrong project: " + baseChange.getProject()); + } else if (!baseChange.getDest().equals(change.getDest())) { + throw new ResourceConflictException( + "base change is targeting wrong branch: " + baseChange.getDest()); + } else if (baseChange.getStatus() == Status.ABANDONED) { + throw new ResourceConflictException( + "base change is abandoned: " + baseChange.getKey()); + } else if (isMergedInto(rw, rsrc.getPatchSet(), basePatchSet)) { + throw new ResourceConflictException( + "base change " + baseChange.getKey() + + " is a descendant of the current change - recursion not allowed"); + } + return basePatchSet.getRevision().get(); } - private PatchSet parseBase(final String base) throws OrmException { + private boolean isMergedInto(RevWalk rw, PatchSet base, PatchSet tip) + throws IOException { + ObjectId baseId = ObjectId.fromString(base.getRevision().get()); + ObjectId tipId = ObjectId.fromString(tip.getRevision().get()); + return rw.isMergedInto(rw.parseCommit(baseId), rw.parseCommit(tipId)); + } + + private PatchSet parseBase(String base) throws OrmException { ReviewDb db = dbProvider.get(); PatchSet.Id basePatchSetId = PatchSet.Id.fromRef(base); if (basePatchSetId != null) { - // try parsing the base as a ref string + // Try parsing the base as a ref string. return db.patchSets().get(basePatchSetId); } - // try parsing base as a change number (assume current patch set) + // Try parsing base as a change number (assume current patch set). PatchSet basePatchSet = null; - try { - Change.Id baseChangeId = Change.Id.parse(base); - if (baseChangeId != null) { - for (PatchSet ps : db.patchSets().byChange(baseChangeId)) { - if (basePatchSet == null || basePatchSet.getId().get() < ps.getId().get()){ - basePatchSet = ps; - } - } - } - } catch (NumberFormatException e) { // probably a SHA1 - } - - // try parsing as SHA1 - if (basePatchSet == null) { - for (PatchSet ps : db.patchSets().byRevision(new RevId(base))) { - if (basePatchSet == null || basePatchSet.getId().get() < ps.getId().get()) { + Integer baseChangeId = Ints.tryParse(base); + if (baseChangeId != null) { + for (PatchSet ps : db.patchSets().byChange(new Change.Id(baseChangeId))) { + if (basePatchSet == null + || basePatchSet.getId().get() < ps.getId().get()) { basePatchSet = ps; } } + if (basePatchSet != null) { + return basePatchSet; + } } + // Try parsing as SHA-1. + for (PatchSet ps : db.patchSets().byRevision(new RevId(base))) { + if (basePatchSet == null + || basePatchSet.getId().get() < ps.getId().get()) { + basePatchSet = ps; + } + } return basePatchSet; } - private boolean hasOneParent(final PatchSet.Id patchSetId) { - try { - // prevent rebase of exotic changes (merge commit, no ancestor). - return (dbProvider.get().patchSetAncestors() - .ancestorsOf(patchSetId).toList().size() == 1); - } catch (OrmException e) { - log.error("Failed to get ancestors of patch set " - + patchSetId.toRefName(), e); - return false; - } + private boolean hasOneParent(RevWalk rw, PatchSet ps) throws IOException { + // Prevent rebase of exotic changes (merge commit, no ancestor). + RevCommit c = rw.parseCommit(ObjectId.fromString(ps.getRevision().get())); + return c.getParentCount() == 1; } @Override public UiAction.Description getDescription(RevisionResource resource) { + Project.NameKey project = resource.getChange().getProject(); + boolean visible = resource.getChange().getStatus().isOpen() + && resource.isCurrent() + && resource.getControl().canRebase(); + if (visible) { + try (Repository repo = repoManager.openRepository(project); + RevWalk rw = new RevWalk(repo)) { + visible = hasOneParent(rw, resource.getPatchSet()); + } catch (IOException e) { + log.error("Failed to get ancestors of patch set " + + resource.getPatchSet().getId(), e); + visible = false; + } + } UiAction.Description descr = new UiAction.Description() .setLabel("Rebase") .setTitle("Rebase onto tip of branch or parent change") - .setVisible(resource.getChange().getStatus().isOpen() - && resource.getControl().canRebase() - && hasOneParent(resource.getPatchSet().getId())); + .setVisible(visible); if (descr.isVisible()) { // Disable the rebase button in the RebaseDialog if // the change cannot be rebased. @@ -240,7 +232,7 @@ @Override public ChangeInfo apply(ChangeResource rsrc, RebaseInput input) throws AuthException, ResourceNotFoundException, - ResourceConflictException, EmailException, OrmException { + ResourceConflictException, EmailException, OrmException, IOException { PatchSet ps = rebase.dbProvider.get().patchSets() .get(rsrc.getChange().currentPatchSetId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java new file mode 100644 index 0000000..96b513e --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java
@@ -0,0 +1,387 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.common.TimeUtil; +import com.google.gerrit.common.errors.EmailException; +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.Change.Status; +import com.google.gerrit.reviewdb.client.ChangeMessage; +import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RevId; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.ChangeUtil; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MergeConflictException; +import com.google.gerrit.server.git.MergeUtil; +import com.google.gerrit.server.project.ChangeControl; +import com.google.gerrit.server.project.InvalidChangeOperationException; +import com.google.gerrit.server.project.NoSuchChangeException; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.ThreeWayMerger; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.TimeZone; + +@Singleton +public class RebaseChange { + private static final Logger log = LoggerFactory.getLogger(RebaseChange.class); + + private final ChangeControl.GenericFactory changeControlFactory; + private final Provider<ReviewDb> db; + private final GitRepositoryManager gitManager; + private final TimeZone serverTimeZone; + private final MergeUtil.Factory mergeUtilFactory; + private final PatchSetInserter.Factory patchSetInserterFactory; + + @Inject + RebaseChange(ChangeControl.GenericFactory changeControlFactory, + Provider<ReviewDb> db, + @GerritPersonIdent PersonIdent myIdent, + GitRepositoryManager gitManager, + MergeUtil.Factory mergeUtilFactory, + PatchSetInserter.Factory patchSetInserterFactory) { + this.changeControlFactory = changeControlFactory; + this.db = db; + this.gitManager = gitManager; + this.serverTimeZone = myIdent.getTimeZone(); + this.mergeUtilFactory = mergeUtilFactory; + this.patchSetInserterFactory = patchSetInserterFactory; + } + + /** + * Rebase the change of the given patch set. + * <p> + * It is verified that the current user is allowed to do the rebase. + * <p> + * If the patch set has no dependency to an open change, then the change is + * rebased on the tip of the destination branch. + * <p> + * If the patch set depends on an open change, it is rebased on the latest + * patch set of this change. + * <p> + * The rebased commit is added as new patch set to the change. + * <p> + * E-mail notification and triggering of hooks happens for the creation of the + * new patch set. + * + * @param git the repository. + * @param rw the RevWalk. + * @param change the change to rebase. + * @param patchSetId the patch set ID to rebase. + * @param uploader the user that creates the rebased patch set. + * @param newBaseRev the commit that should be the new base. + * @throws NoSuchChangeException if the change to which the patch set belongs + * does not exist or is not visible to the user. + * @throws EmailException if sending the e-mail to notify about the new patch + * set fails. + * @throws OrmException if accessing the database fails. + * @throws IOException if accessing the repository fails. + * @throws InvalidChangeOperationException if rebase is not possible or not + * allowed. + */ + public void rebase(Repository git, RevWalk rw, Change change, + PatchSet.Id patchSetId, IdentifiedUser uploader, String newBaseRev) + throws NoSuchChangeException, EmailException, OrmException, IOException, + InvalidChangeOperationException { + Change.Id changeId = patchSetId.getParentKey(); + ChangeControl changeControl = + changeControlFactory.validateFor(change, uploader); + if (!changeControl.canRebase()) { + throw new InvalidChangeOperationException("Cannot rebase: New patch sets" + + " are not allowed to be added to change: " + changeId); + } + try (ObjectInserter inserter = git.newObjectInserter()) { + String baseRev = newBaseRev; + if (baseRev == null) { + baseRev = findBaseRevision( + patchSetId, db.get(), change.getDest(), git, rw); + } + ObjectId baseObjectId = git.resolve(baseRev); + if (baseObjectId == null) { + throw new InvalidChangeOperationException( + "Cannot rebase: Failed to resolve baseRev: " + baseRev); + } + RevCommit baseCommit = rw.parseCommit(baseObjectId); + + PersonIdent committerIdent = + uploader.newCommitterIdent(TimeUtil.nowTs(), serverTimeZone); + + rebase(git, rw, inserter, change, patchSetId, + uploader, baseCommit, mergeUtilFactory.create( + changeControl.getProjectControl().getProjectState(), true), + committerIdent, true, ValidatePolicy.GERRIT); + } catch (MergeConflictException e) { + throw new IOException(e.getMessage()); + } + } + + /** + * Find the commit onto which a patch set should be rebased. + * <p> + * This is defined as the latest patch set of the change corresponding to + * this commit's parent, or the destination branch tip in the case where the + * parent's change is merged. + * + * @param patchSetId patch set ID for which the new base commit should be + * found. + * @param db the ReviewDb. + * @param destBranch the destination branch. + * @param git the repository. + * @param rw the RevWalk. + * @return the commit onto which the patch set should be rebased. + * @throws InvalidChangeOperationException if rebase is not possible or not + * allowed. + * @throws IOException if accessing the repository fails. + * @throws OrmException if accessing the database fails. + */ + private static String findBaseRevision(PatchSet.Id patchSetId, + ReviewDb db, Branch.NameKey destBranch, Repository git, RevWalk rw) + throws InvalidChangeOperationException, IOException, OrmException { + String baseRev = null; + + PatchSet patchSet = db.patchSets().get(patchSetId); + if (patchSet == null) { + throw new InvalidChangeOperationException( + "Patch set " + patchSetId + " not found"); + } + RevCommit commit = rw.parseCommit( + ObjectId.fromString(patchSet.getRevision().get())); + + if (commit.getParentCount() > 1) { + throw new InvalidChangeOperationException( + "Cannot rebase a change with multiple parents."); + } else if (commit.getParentCount() == 0) { + throw new InvalidChangeOperationException( + "Cannot rebase a change without any parents" + + " (is this the initial commit?)."); + } + + RevId parentRev = new RevId(commit.getParent(0).name()); + + for (PatchSet depPatchSet : db.patchSets().byRevision(parentRev)) { + Change.Id depChangeId = depPatchSet.getId().getParentKey(); + Change depChange = db.changes().get(depChangeId); + if (!depChange.getDest().equals(destBranch)) { + continue; + } + + if (depChange.getStatus() == Status.ABANDONED) { + throw new InvalidChangeOperationException( + "Cannot rebase a change with an abandoned parent: " + + depChange.getKey()); + } + + if (depChange.getStatus().isOpen()) { + if (depPatchSet.getId().equals(depChange.currentPatchSetId())) { + throw new InvalidChangeOperationException( + "Change is already based on the latest patch set of the" + + " dependent change."); + } + PatchSet latestDepPatchSet = + db.patchSets().get(depChange.currentPatchSetId()); + baseRev = latestDepPatchSet.getRevision().get(); + } + break; + } + + if (baseRev == null) { + // We are dependent on a merged PatchSet or have no PatchSet + // dependencies at all. + Ref destRef = git.getRef(destBranch.get()); + if (destRef == null) { + throw new InvalidChangeOperationException( + "The destination branch does not exist: " + destBranch.get()); + } + baseRev = destRef.getObjectId().getName(); + if (baseRev.equals(parentRev.get())) { + throw new InvalidChangeOperationException( + "Change is already up to date."); + } + } + return baseRev; + } + + /** + * Rebase the change of the given patch set on the given base commit. + * <p> + * The rebased commit is added as new patch set to the change. + * <p> + * E-mail notification and triggering of hooks is only done for the creation + * of the new patch set if {@code sendEmail} and {@code runHooks} are true, + * respectively. + * + * @param git the repository. + * @param inserter the object inserter. + * @param change the change to rebase. + * @param patchSetId the patch set ID to rebase. + * @param uploader the user that creates the rebased patch set. + * @param baseCommit the commit that should be the new base. + * @param mergeUtil merge utilities for the destination project. + * @param committerIdent the committer's identity. + * @param runHooks if hooks should be run for the new patch set. + * @param validate if commit validation should be run for the new patch set. + * @param rw the RevWalk. + * @return the new patch set, which is based on the given base commit. + * @throws NoSuchChangeException if the change to which the patch set belongs + * does not exist or is not visible to the user. + * @throws OrmException if accessing the database fails. + * @throws IOException if rebase is not possible. + * @throws InvalidChangeOperationException if rebase is not possible or not + * allowed. + */ + public PatchSet rebase(Repository git, RevWalk rw, + ObjectInserter inserter, Change change, PatchSet.Id patchSetId, + IdentifiedUser uploader, RevCommit baseCommit, MergeUtil mergeUtil, + PersonIdent committerIdent, boolean runHooks, ValidatePolicy validate) + throws NoSuchChangeException, OrmException, IOException, + InvalidChangeOperationException, MergeConflictException { + if (!change.currentPatchSetId().equals(patchSetId)) { + throw new InvalidChangeOperationException("patch set is not current"); + } + PatchSet originalPatchSet = db.get().patchSets().get(patchSetId); + + RevCommit rebasedCommit; + ObjectId oldId = ObjectId.fromString(originalPatchSet.getRevision().get()); + ObjectId newId = rebaseCommit(git, inserter, rw.parseCommit(oldId), + baseCommit, mergeUtil, committerIdent); + + rebasedCommit = rw.parseCommit(newId); + + ChangeControl changeControl = + changeControlFactory.validateFor(change, uploader); + + PatchSetInserter patchSetInserter = patchSetInserterFactory + .create(git, rw, changeControl, rebasedCommit) + .setValidatePolicy(validate) + .setDraft(originalPatchSet.isDraft()) + .setUploader(uploader.getAccountId()) + .setSendMail(false) + .setRunHooks(runHooks); + + PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId(); + ChangeMessage cmsg = new ChangeMessage( + new ChangeMessage.Key(change.getId(), + ChangeUtil.messageUUID(db.get())), uploader.getAccountId(), + TimeUtil.nowTs(), patchSetId); + + cmsg.setMessage("Patch Set " + newPatchSetId.get() + + ": Patch Set " + patchSetId.get() + " was rebased"); + + Change newChange = patchSetInserter + .setMessage(cmsg) + .insert(); + + return db.get().patchSets().get(newChange.currentPatchSetId()); + } + + /** + * Rebase a commit. + * + * @param git repository to find commits in. + * @param inserter inserter to handle new trees and blobs. + * @param original the commit to rebase. + * @param base base to rebase against. + * @param mergeUtil merge utilities for the destination project. + * @param committerIdent committer identity. + * @return the id of the rebased commit. + * @throws MergeConflictException the rebase failed due to a merge conflict. + * @throws IOException the merge failed for another reason. + */ + private ObjectId rebaseCommit(Repository git, ObjectInserter inserter, + RevCommit original, RevCommit base, MergeUtil mergeUtil, + PersonIdent committerIdent) throws MergeConflictException, IOException, + InvalidChangeOperationException { + RevCommit parentCommit = original.getParent(0); + + if (base.equals(parentCommit)) { + throw new InvalidChangeOperationException( + "Change is already up to date."); + } + + ThreeWayMerger merger = mergeUtil.newThreeWayMerger(git, inserter); + merger.setBase(parentCommit); + merger.merge(original, base); + + if (merger.getResultTreeId() == null) { + throw new MergeConflictException( + "The change could not be rebased due to a conflict during merge."); + } + + CommitBuilder cb = new CommitBuilder(); + cb.setTreeId(merger.getResultTreeId()); + cb.setParentId(base); + cb.setAuthor(original.getAuthorIdent()); + cb.setMessage(original.getFullMessage()); + cb.setCommitter(committerIdent); + ObjectId objectId = inserter.insert(cb); + inserter.flush(); + return objectId; + } + + public boolean canRebase(ChangeResource r) { + Change c = r.getChange(); + return canRebase(c.getProject(), c.currentPatchSetId(), c.getDest()); + } + + public boolean canRebase(RevisionResource r) { + return canRebase(r.getChange().getProject(), + r.getPatchSet().getId(), r.getChange().getDest()); + } + + public boolean canRebase(Project.NameKey project, PatchSet.Id patchSetId, + Branch.NameKey branch) { + Repository git; + try { + git = gitManager.openRepository(project); + } catch (RepositoryNotFoundException err) { + return false; + } catch (IOException err) { + return false; + } + try (RevWalk rw = new RevWalk(git)) { + findBaseRevision(patchSetId, db.get(), branch, git, rw); + return true; + } catch (InvalidChangeOperationException e) { + return false; + } catch (OrmException | IOException e) { + log.warn("Error checking if patch set " + patchSetId + " on " + branch + + " can be rebased", e); + return false; + } finally { + git.close(); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java index cce362a..46d73d7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
@@ -15,7 +15,6 @@ package com.google.gerrit.server.change; import com.google.common.base.Strings; -import com.google.common.util.concurrent.CheckedFuture; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.extensions.api.changes.RestoreInput; import com.google.gerrit.extensions.common.ChangeInfo; @@ -122,17 +121,16 @@ } update.commit(); - CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId()); + indexer.index(db, change); try { - ReplyToChangeSender cm = restoredSenderFactory.create(change); + ReplyToChangeSender cm = restoredSenderFactory.create(change.getId()); cm.setFrom(caller.getAccountId()); cm.setChangeMessage(message); cm.send(); } catch (Exception e) { log.error("Cannot email update for change " + change.getChangeId(), e); } - f.checkedGet(); hooks.doChangeRestoredHook(change, caller.getAccount(), db.patchSets().get(change.currentPatchSetId()),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java index c4c9186..9e243b0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java
@@ -49,7 +49,6 @@ import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.RAMDirectory; -import org.apache.lucene.util.Version; import org.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,8 +62,8 @@ /** * The suggest oracle may be called many times in rapid succession during the * course of one operation. - * It would be easy to have a simple Cache<Boolean, List<Account>> with a short - * expiration time of 30s. + * It would be easy to have a simple {@code Cache<Boolean, List<Account>>} + * with a short expiration time of 30s. * Cache only has a single key we're just using Cache for the expiration behavior. */ @Singleton @@ -147,9 +146,7 @@ private IndexSearcher index() throws IOException, OrmException { RAMDirectory idx = new RAMDirectory(); - @SuppressWarnings("deprecation") IndexWriterConfig config = new IndexWriterConfig( - Version.LUCENE_4_10_1, new StandardAnalyzer(CharArraySet.EMPTY_SET)); config.setOpenMode(OpenMode.CREATE);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java index e58d1a1..538f36b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java
@@ -92,7 +92,7 @@ return this; } - Optional<ChangeEdit> getEdit() { + public Optional<ChangeEdit> getEdit() { return edit; } @@ -104,4 +104,8 @@ } return s; } + + public boolean isCurrent() { + return ps.getId().equals(getChange().currentPatchSetId()); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java index 5f67e6e..109d96f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -99,6 +99,10 @@ "Other changes in this topic are not ready"; private static final String BLOCKED_HIDDEN_TOPIC_TOOLTIP = "Other hidden changes in this topic are not ready"; + private static final String CLICK_FAILURE_OTHER_TOOLTIP = + "Clicking the button would fail for other changes in the topic"; + private static final String CLICK_FAILURE_TOOLTIP = + "Clicking the button would fail."; public enum Status { SUBMITTED, MERGED @@ -169,7 +173,7 @@ this.titlePattern = new ParameterizedString(MoreObjects.firstNonNull( cfg.getString("change", null, "submitTooltip"), DEFAULT_TOOLTIP)); - submitWholeTopic = cfg.getBoolean("change", null, "submitWholeTopic" , false); + submitWholeTopic = wholeTopicEnabled(cfg); this.submitTopicLabel = MoreObjects.firstNonNull( Strings.emptyToNull(cfg.getString("change", null, "submitTopicLabel")), "Submit whole topic"); @@ -206,17 +210,19 @@ rsrc.getPatchSet().getRevision().get())); } - change = submit(rsrc, caller, false); - if (change == null) { - throw new ResourceConflictException("change is " - + status(dbProvider.get().changes().get(rsrc.getChange().getId()))); - } + List<Change> submittedChanges = submit(rsrc, caller, false); if (input.waitForMerge) { - mergeQueue.merge(change.getDest()); + for (Change c : submittedChanges) { + // TODO(sbeller): We should make schedule return a Future, then we + // could do these all in parallel and still block until they're done. + mergeQueue.merge(c.getDest()); + } change = dbProvider.get().changes().get(change.getId()); } else { - mergeQueue.schedule(change.getDest()); + for (Change c : submittedChanges) { + mergeQueue.schedule(c.getDest()); + } } if (change == null) { @@ -239,14 +245,15 @@ } /** - * @param changes list of changes to be submitted at once + * @param changeList list of changes to be submitted at once * @param identifiedUser the user who is checking to submit * @return a reason why any of the changes is not submittable or null */ - private String problemsForSubmittingChanges(List<ChangeData> changes, + private String problemsForSubmittingChanges( + List<ChangeData> changeList, IdentifiedUser identifiedUser) { - for (ChangeData c : changes) { - try { + try { + for (ChangeData c : changeList) { ChangeControl changeControl = c.changeControl().forUser( identifiedUser); if (!changeControl.isVisible(dbProvider.get())) { @@ -255,13 +262,26 @@ if (!changeControl.canSubmit()) { return BLOCKED_TOPIC_TOOLTIP; } + // Recheck mergeability rather than using value stored in the index, + // which may be stale. + // TODO(dborowitz): This is ugly; consider providing a way to not read + // stored fields from the index in the first place. + c.setMergeable(null); + Boolean mergeable = c.isMergeable(); + if (mergeable == null) { + log.error("Ephemeral error checking if change is submittable"); + return CLICK_FAILURE_TOOLTIP; + } + if (!mergeable) { + return CLICK_FAILURE_OTHER_TOOLTIP; + } checkSubmitRule(c, c.currentPatchSet(), false); - } catch (OrmException e) { - log.error("Error checking if change is submittable", e); - throw new OrmRuntimeException(e); - } catch (ResourceConflictException e) { - return BLOCKED_TOPIC_TOOLTIP; } + } catch (ResourceConflictException e) { + return BLOCKED_TOPIC_TOOLTIP; + } catch (OrmException e) { + log.error("Error checking if change is submittable", e); + throw new OrmRuntimeException("Could not determine problems for the change", e); } return null; } @@ -274,12 +294,16 @@ && resource.getChange().getStatus().isOpen() && resource.getPatchSet().getId().equals(current) && resource.getControl().canSubmit(); - ReviewDb db = dbProvider.get(); ChangeData cd = changeDataFactory.create(db, resource.getControl()); - if (problemsForSubmittingChanges(Arrays.asList(cd), resource.getUser()) - != null) { + + try { + checkSubmitRule(cd, cd.currentPatchSet(), false); + } catch (ResourceConflictException e) { visible = false; + } catch (OrmException e) { + log.error("Error checking if change is submittable", e); + throw new OrmRuntimeException("Could not determine problems for the change", e); } if (!visible) { @@ -288,13 +312,21 @@ .setTitle("") .setVisible(false); } + + Boolean enabled; + try { + enabled = cd.isMergeable(); + } catch (OrmException e) { + throw new OrmRuntimeException("Could not determine mergeability", e); + } + + List<ChangeData> changesByTopic = null; if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) { - List<ChangeData> changesByTopic = null; - try { - changesByTopic = queryProvider.get().byTopicOpen(topic); - } catch (OrmException e) { - throw new OrmRuntimeException(e); - } + changesByTopic = getChangesByTopic(topic); + } + if (submitWholeTopic + && !Strings.isNullOrEmpty(topic) + && changesByTopic.size() > 1) { Map<String, String> params = ImmutableMap.of( "topicSize", String.valueOf(changesByTopic.size())); String topicProblems = problemsForSubmittingChanges(changesByTopic, @@ -311,7 +343,7 @@ .setTitle(Strings.emptyToNull( submitTopicTooltip.replace(params))) .setVisible(true) - .setEnabled(true); + .setEnabled(Boolean.TRUE.equals(enabled)); } } else { RevId revId = resource.getPatchSet().getRevision(); @@ -322,7 +354,8 @@ return new UiAction.Description() .setLabel(label) .setTitle(Strings.emptyToNull(titlePattern.replace(params))) - .setVisible(true); + .setVisible(true) + .setEnabled(Boolean.TRUE.equals(enabled)); } } @@ -345,9 +378,10 @@ .orNull(); } - private Change submitToDatabase(ReviewDb db, Change.Id changeId, - final Timestamp timestamp) throws OrmException { - return db.changes().atomicUpdate(changeId, + private Change submitToDatabase(final ReviewDb db, final Change.Id changeId, + final Timestamp timestamp) throws OrmException, + ResourceConflictException { + Change ret = db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() { @Override public Change update(Change change) { @@ -359,6 +393,12 @@ return null; } }); + if (ret != null) { + return ret; + } else { + throw new ResourceConflictException("change " + changeId + " is " + + status(db.changes().get(changeId))); + } } private Change submitThisChange(RevisionResource rsrc, IdentifiedUser caller, @@ -376,13 +416,11 @@ db.changes().beginTransaction(change.getId()); try { - BatchMetaDataUpdate batch = approve(rsrc, update, caller, timestamp); + BatchMetaDataUpdate batch = approve(rsrc.getPatchSet().getId(), + cd.changeControl(), update, caller, timestamp); // Write update commit after all normalized label commits. batch.write(update, new CommitBuilder()); change = submitToDatabase(db, change.getId(), timestamp); - if (change == null) { - return null; - } db.commit(); } finally { db.rollback(); @@ -391,7 +429,7 @@ return change; } - private Change submitWholeTopic(RevisionResource rsrc, IdentifiedUser caller, + private List<Change> submitWholeTopic(RevisionResource rsrc, IdentifiedUser caller, boolean force, String topic) throws ResourceConflictException, OrmException, IOException { Preconditions.checkNotNull(topic); @@ -415,45 +453,45 @@ db.changes().beginTransaction(change.getId()); try { - BatchMetaDataUpdate batch = approve(rsrc, update, caller, timestamp); - // Write update commit after all normalized label commits. - batch.write(update, new CommitBuilder()); - for (ChangeData c : changesByTopic) { - if (submitToDatabase(db, c.getId(), timestamp) == null) { - return null; - } + BatchMetaDataUpdate batch = approve(c.currentPatchSet().getId(), + c.changeControl(), update, caller, timestamp); + // Write update commit after all normalized label commits. + batch.write(update, new CommitBuilder()); + submitToDatabase(db, c.getId(), timestamp); } db.commit(); } finally { db.rollback(); } List<Change.Id> ids = new ArrayList<>(changesByTopic.size()); + List<Change> ret = new ArrayList<>(changesByTopic.size()); for (ChangeData c : changesByTopic) { ids.add(c.getId()); + ret.add(c.change()); } indexer.indexAsync(ids).checkedGet(); - return change; + + return ret; } - public Change submit(RevisionResource rsrc, IdentifiedUser caller, + public List<Change> submit(RevisionResource rsrc, IdentifiedUser caller, boolean force) throws ResourceConflictException, OrmException, IOException { String topic = rsrc.getChange().getTopic(); if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) { return submitWholeTopic(rsrc, caller, force, topic); } else { - return submitThisChange(rsrc, caller, force); + return Arrays.asList(submitThisChange(rsrc, caller, force)); } } - private BatchMetaDataUpdate approve(RevisionResource rsrc, + private BatchMetaDataUpdate approve(PatchSet.Id psId, ChangeControl control, ChangeUpdate update, IdentifiedUser caller, Timestamp timestamp) throws OrmException { - PatchSet.Id psId = rsrc.getPatchSet().getId(); Map<PatchSetApproval.Key, PatchSetApproval> byKey = Maps.newHashMap(); for (PatchSetApproval psa : - approvalsUtil.byPatchSet(dbProvider.get(), rsrc.getControl(), psId)) { + approvalsUtil.byPatchSet(dbProvider.get(), control, psId)) { if (!byKey.containsKey(psa.getKey())) { byKey.put(psa.getKey(), psa); } @@ -464,7 +502,7 @@ || !submit.getAccountId().equals(caller.getAccountId())) { submit = new PatchSetApproval( new PatchSetApproval.Key( - rsrc.getPatchSet().getId(), + psId, caller.getAccountId(), LabelId.SUBMIT), (short) 1, TimeUtil.nowTs()); @@ -479,7 +517,7 @@ // was added. So we need to make sure votes are accurate now. This way if // permissions get modified in the future, historical records stay accurate. LabelNormalizer.Result normalized = - labelNormalizer.normalize(rsrc.getControl(), byKey.values()); + labelNormalizer.normalize(control, byKey.values()); // TODO(dborowitz): Don't use a label in notedb; just check when status // change happened. @@ -489,13 +527,13 @@ dbProvider.get().patchSetApprovals().delete(normalized.deleted()); try { - return saveToBatch(rsrc, update, normalized, timestamp); + return saveToBatch(control, update, normalized, timestamp); } catch (IOException e) { throw new OrmException(e); } } - private BatchMetaDataUpdate saveToBatch(RevisionResource rsrc, + private BatchMetaDataUpdate saveToBatch(ChangeControl ctl, ChangeUpdate callerUpdate, LabelNormalizer.Result normalized, Timestamp timestamp) throws IOException { Table<Account.Id, String, Optional<Short>> byUser = HashBasedTable.create(); @@ -507,7 +545,6 @@ byUser.put(psa.getAccountId(), psa.getLabel(), Optional.<Short> absent()); } - ChangeControl ctl = rsrc.getControl(); BatchMetaDataUpdate batch = callerUpdate.openUpdate(); for (Account.Id accountId : byUser.rowKeySet()) { if (!accountId.equals(callerUpdate.getUser().getAccountId())) { @@ -654,6 +691,18 @@ return new RevisionResource(changes.parse(target), rsrc.getPatchSet()); } + static boolean wholeTopicEnabled(Config config) { + return config.getBoolean("change", null, "submitWholeTopic" , false); + } + + private List<ChangeData> getChangesByTopic(String topic) { + try { + return queryProvider.get().byTopicOpen(topic); + } catch (OrmException e) { + throw new OrmRuntimeException(e); + } + } + public static class CurrentRevision implements RestModifyView<ChangeResource, SubmitInput> { private final Provider<ReviewDb> dbProvider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java index d3c4132..31e34cf 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
@@ -106,7 +106,7 @@ Map<String, None> need; Map<String, AccountInfo> may; Map<String, None> impossible; - Integer prologReductionCount; + Long prologReductionCount; Record(SubmitRecord r, AccountLoader accounts) { this.status = r.status;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java deleted file mode 100644 index 67e70c1..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java +++ /dev/null
@@ -1,399 +0,0 @@ -// Copyright (C) 2012 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.changedetail; - -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.common.errors.EmailException; -import com.google.gerrit.reviewdb.client.Branch; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.Change.Status; -import com.google.gerrit.reviewdb.client.ChangeMessage; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.client.PatchSetAncestor; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RevId; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.ChangeUtil; -import com.google.gerrit.server.GerritPersonIdent; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.change.ChangeResource; -import com.google.gerrit.server.change.PatchSetInserter; -import com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy; -import com.google.gerrit.server.change.RevisionResource; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.MergeConflictException; -import com.google.gerrit.server.git.MergeUtil; -import com.google.gerrit.server.project.ChangeControl; -import com.google.gerrit.server.project.InvalidChangeOperationException; -import com.google.gerrit.server.project.NoSuchChangeException; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; - -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.merge.ThreeWayMerger; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; - -import java.io.IOException; -import java.util.List; -import java.util.TimeZone; - -@Singleton -public class RebaseChange { - private final ChangeControl.GenericFactory changeControlFactory; - private final Provider<ReviewDb> db; - private final GitRepositoryManager gitManager; - private final TimeZone serverTimeZone; - private final MergeUtil.Factory mergeUtilFactory; - private final PatchSetInserter.Factory patchSetInserterFactory; - - @Inject - RebaseChange(final ChangeControl.GenericFactory changeControlFactory, - final Provider<ReviewDb> db, - @GerritPersonIdent final PersonIdent myIdent, - final GitRepositoryManager gitManager, - final MergeUtil.Factory mergeUtilFactory, - final PatchSetInserter.Factory patchSetInserterFactory) { - this.changeControlFactory = changeControlFactory; - this.db = db; - this.gitManager = gitManager; - this.serverTimeZone = myIdent.getTimeZone(); - this.mergeUtilFactory = mergeUtilFactory; - this.patchSetInserterFactory = patchSetInserterFactory; - } - - /** - * Rebases the change of the given patch set. - * - * It is verified that the current user is allowed to do the rebase. - * - * If the patch set has no dependency to an open change, then the change is - * rebased on the tip of the destination branch. - * - * If the patch set depends on an open change, it is rebased on the latest - * patch set of this change. - * - * The rebased commit is added as new patch set to the change. - * - * E-mail notification and triggering of hooks happens for the creation of the - * new patch set. - * - * @param change the change to perform the rebase for - * @param patchSetId the id of the patch set - * @param uploader the user that creates the rebased patch set - * @param newBaseRev the commit that should be the new base - * @throws NoSuchChangeException thrown if the change to which the patch set - * belongs does not exist or is not visible to the user - * @throws EmailException thrown if sending the e-mail to notify about the new - * patch set fails - * @throws OrmException thrown in case accessing the database fails - * @throws IOException thrown if rebase is not possible or not needed - * @throws InvalidChangeOperationException thrown if rebase is not allowed - */ - public void rebase(Change change, PatchSet.Id patchSetId, final IdentifiedUser uploader, - final String newBaseRev) throws NoSuchChangeException, EmailException, OrmException, - IOException, InvalidChangeOperationException { - final Change.Id changeId = patchSetId.getParentKey(); - final ChangeControl changeControl = - changeControlFactory.validateFor(change, uploader); - if (!changeControl.canRebase()) { - throw new InvalidChangeOperationException( - "Cannot rebase: New patch sets are not allowed to be added to change: " - + changeId.toString()); - } - try (Repository git = gitManager.openRepository(change.getProject()); - RevWalk rw = new RevWalk(git); - ObjectInserter inserter = git.newObjectInserter()) { - String baseRev = newBaseRev; - if (baseRev == null) { - baseRev = findBaseRevision(patchSetId, db.get(), - change.getDest(), git, null, null, null); - } - ObjectId baseObjectId = git.resolve(baseRev); - if (baseObjectId == null) { - throw new InvalidChangeOperationException( - "Cannot rebase: Failed to resolve baseRev: " + baseRev); - } - final RevCommit baseCommit = rw.parseCommit(baseObjectId); - - PersonIdent committerIdent = - uploader.newCommitterIdent(TimeUtil.nowTs(), - serverTimeZone); - - rebase(git, rw, inserter, patchSetId, change, - uploader, baseCommit, mergeUtilFactory.create( - changeControl.getProjectControl().getProjectState(), true), - committerIdent, true, ValidatePolicy.GERRIT); - } catch (MergeConflictException e) { - throw new IOException(e.getMessage()); - } - } - - /** - * Finds the revision of commit on which the given patch set should be based. - * - * @param patchSetId the id of the patch set for which the new base commit - * should be found - * @param db the ReviewDb - * @param destBranch the destination branch - * @param git the repository - * @param patchSetAncestors the original PatchSetAncestor of the given patch - * set that should be based - * @param depPatchSetList the original patch set list on which the rebased - * patch set depends - * @param depChangeList the original change list on whose patch set the - * rebased patch set depends - * @return the revision of commit on which the given patch set should be based - * @throws IOException thrown if rebase is not possible or not needed - * @throws OrmException thrown in case accessing the database fails - */ - private static String findBaseRevision(final PatchSet.Id patchSetId, - final ReviewDb db, final Branch.NameKey destBranch, final Repository git, - List<PatchSetAncestor> patchSetAncestors, List<PatchSet> depPatchSetList, - List<Change> depChangeList) throws IOException, OrmException { - - String baseRev = null; - - if (patchSetAncestors == null) { - patchSetAncestors = - db.patchSetAncestors().ancestorsOf(patchSetId).toList(); - } - - if (patchSetAncestors.size() > 1) { - throw new IOException( - "Cannot rebase a change with multiple parents. Parent commits: " - + patchSetAncestors.toString()); - } - if (patchSetAncestors.size() == 0) { - throw new IOException( - "Cannot rebase a change without any parents (is this the initial commit?)."); - } - - RevId ancestorRev = patchSetAncestors.get(0).getAncestorRevision(); - if (depPatchSetList == null || depPatchSetList.size() != 1 || - !depPatchSetList.get(0).getRevision().equals(ancestorRev)) { - depPatchSetList = db.patchSets().byRevision(ancestorRev).toList(); - } - - for (PatchSet depPatchSet : depPatchSetList) { - - Change.Id depChangeId = depPatchSet.getId().getParentKey(); - Change depChange; - if (depChangeList == null || depChangeList.size() != 1 || - !depChangeList.get(0).getId().equals(depChangeId)) { - depChange = db.changes().get(depChangeId); - } else { - depChange = depChangeList.get(0); - } - if (!depChange.getDest().equals(destBranch)) { - continue; - } - - if (depChange.getStatus() == Status.ABANDONED) { - throw new IOException("Cannot rebase a change with an abandoned parent: " - + depChange.getKey().toString()); - } - - if (depChange.getStatus().isOpen()) { - if (depPatchSet.getId().equals(depChange.currentPatchSetId())) { - throw new IOException( - "Change is already based on the latest patch set of the dependent change."); - } - PatchSet latestDepPatchSet = - db.patchSets().get(depChange.currentPatchSetId()); - baseRev = latestDepPatchSet.getRevision().get(); - } - break; - } - - if (baseRev == null) { - // We are dependent on a merged PatchSet or have no PatchSet - // dependencies at all. - Ref destRef = git.getRef(destBranch.get()); - if (destRef == null) { - throw new IOException( - "The destination branch does not exist: " - + destBranch.get()); - } - baseRev = destRef.getObjectId().getName(); - if (baseRev.equals(ancestorRev.get())) { - throw new IOException("Change is already up to date."); - } - } - return baseRev; - } - - /** - * Rebases the change of the given patch set on the given base commit. - * - * The rebased commit is added as new patch set to the change. - * - * E-mail notification and triggering of hooks is only done for the creation of - * the new patch set if `sendEmail` and `runHooks` are set to true. - * - * @param git the repository - * @param revWalk the RevWalk - * @param inserter the object inserter - * @param patchSetId the id of the patch set - * @param change the change that should be rebased - * @param uploader the user that creates the rebased patch set - * @param baseCommit the commit that should be the new base - * @param mergeUtil merge utilities for the destination project - * @param committerIdent the committer's identity - * @param runHooks if hooks should be run for the new patch set - * @param validate if commit validation should be run for the new patch set - * @return the new patch set which is based on the given base commit - * @throws NoSuchChangeException thrown if the change to which the patch set - * belongs does not exist or is not visible to the user - * @throws OrmException thrown in case accessing the database fails - * @throws IOException thrown if rebase is not possible or not needed - * @throws InvalidChangeOperationException thrown if rebase is not allowed - */ - public PatchSet rebase(final Repository git, final RevWalk revWalk, - final ObjectInserter inserter, final PatchSet.Id patchSetId, - final Change change, final IdentifiedUser uploader, final RevCommit baseCommit, - final MergeUtil mergeUtil, PersonIdent committerIdent, - boolean runHooks, ValidatePolicy validate) - throws NoSuchChangeException, - OrmException, IOException, InvalidChangeOperationException, - MergeConflictException { - if (!change.currentPatchSetId().equals(patchSetId)) { - throw new InvalidChangeOperationException("patch set is not current"); - } - final PatchSet originalPatchSet = db.get().patchSets().get(patchSetId); - - final RevCommit rebasedCommit; - ObjectId oldId = ObjectId.fromString(originalPatchSet.getRevision().get()); - ObjectId newId = rebaseCommit(git, inserter, revWalk.parseCommit(oldId), - baseCommit, mergeUtil, committerIdent); - - rebasedCommit = revWalk.parseCommit(newId); - - final ChangeControl changeControl = - changeControlFactory.validateFor(change, uploader); - - PatchSetInserter patchSetInserter = patchSetInserterFactory - .create(git, revWalk, changeControl, rebasedCommit) - .setValidatePolicy(validate) - .setDraft(originalPatchSet.isDraft()) - .setUploader(uploader.getAccountId()) - .setSendMail(false) - .setRunHooks(runHooks); - - final PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId(); - final ChangeMessage cmsg = new ChangeMessage( - new ChangeMessage.Key(change.getId(), - ChangeUtil.messageUUID(db.get())), uploader.getAccountId(), - TimeUtil.nowTs(), patchSetId); - - cmsg.setMessage("Patch Set " + newPatchSetId.get() - + ": Patch Set " + patchSetId.get() + " was rebased"); - - Change newChange = patchSetInserter - .setMessage(cmsg) - .insert(); - - return db.get().patchSets().get(newChange.currentPatchSetId()); - } - - /** - * Rebase a commit. - * - * @param git repository to find commits in. - * @param inserter inserter to handle new trees and blobs. - * @param original the commit to rebase. - * @param base base to rebase against. - * @param mergeUtil merge utilities for the destination project. - * @param committerIdent committer identity. - * @return the id of the rebased commit. - * @throws MergeConflictException the rebase failed due to a merge conflict. - * @throws IOException the merge failed for another reason. - */ - private ObjectId rebaseCommit(Repository git, ObjectInserter inserter, - RevCommit original, RevCommit base, MergeUtil mergeUtil, - PersonIdent committerIdent) throws MergeConflictException, IOException { - RevCommit parentCommit = original.getParent(0); - - if (base.equals(parentCommit)) { - throw new IOException("Change is already up to date."); - } - - ThreeWayMerger merger = mergeUtil.newThreeWayMerger(git, inserter); - merger.setBase(parentCommit); - merger.merge(original, base); - - if (merger.getResultTreeId() == null) { - throw new MergeConflictException( - "The change could not be rebased due to a conflict during merge."); - } - - CommitBuilder cb = new CommitBuilder(); - cb.setTreeId(merger.getResultTreeId()); - cb.setParentId(base); - cb.setAuthor(original.getAuthorIdent()); - cb.setMessage(original.getFullMessage()); - cb.setCommitter(committerIdent); - ObjectId objectId = inserter.insert(cb); - inserter.flush(); - return objectId; - } - - public boolean canRebase(ChangeResource r) { - Change c = r.getChange(); - return canRebase(c.getProject(), c.currentPatchSetId(), c.getDest()); - } - - public boolean canRebase(RevisionResource r) { - return canRebase(r.getChange().getProject(), - r.getPatchSet().getId(), r.getChange().getDest()); - } - - public boolean canRebase(Project.NameKey project, - PatchSet.Id patchSetId, Branch.NameKey branch) { - Repository git; - try { - git = gitManager.openRepository(project); - } catch (RepositoryNotFoundException err) { - return false; - } catch (IOException err) { - return false; - } - try { - findBaseRevision( - patchSetId, - db.get(), - branch, - git, - null, - null, - null); - return true; - } catch (IOException e) { - return false; - } catch (OrmException e) { - return false; - } finally { - git.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java index d7138b3..abcd441 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -44,6 +44,7 @@ private final boolean enableRunAs; private final boolean userNameToLowerCase; private final boolean gitBasicAuth; + private final boolean useContributorAgreements; private final String loginUrl; private final String logoutUrl; private final String openIdSsoUrl; @@ -75,6 +76,8 @@ trustContainerAuth = cfg.getBoolean("auth", "trustContainerAuth", false); enableRunAs = cfg.getBoolean("auth", null, "enableRunAs", true); gitBasicAuth = cfg.getBoolean("auth", "gitBasicAuth", false); + useContributorAgreements = + cfg.getBoolean("auth", "contributoragreements", false); userNameToLowerCase = cfg.getBoolean("auth", "userNameToLowerCase", false); @@ -194,6 +197,11 @@ return gitBasicAuth; } + /** Whether contributor agreements are used. */ + public boolean isUseContributorAgreements() { + return useContributorAgreements; + } + public boolean isIdentityTrustable(final Collection<AccountExternalId> ids) { switch (getAuthType()) { case DEVELOPMENT_BECOME_ANY_ACCOUNT:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadConfig.java index 2d9f21a..4986989 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadConfig.java
@@ -16,12 +16,14 @@ import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; +import com.google.gerrit.server.change.ArchiveFormat; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.jgit.lib.Config; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -31,6 +33,7 @@ public class DownloadConfig { private final Set<DownloadScheme> downloadSchemes; private final Set<DownloadCommand> downloadCommands; + private final Set<ArchiveFormat> archiveFormats; @Inject DownloadConfig(@GerritServerConfig final Config cfg) { @@ -45,6 +48,17 @@ DownloadCommand.DEFAULT_DOWNLOADS); downloadCommands = Collections.unmodifiableSet(new HashSet<>(allCommands)); + + String v = cfg.getString("download", null, "archive"); + if (v == null) { + archiveFormats = EnumSet.allOf(ArchiveFormat.class); + } else if (v.isEmpty() || "off".equalsIgnoreCase(v)) { + archiveFormats = Collections.emptySet(); + } else { + archiveFormats = new HashSet<>(ConfigUtil.getEnumList(cfg, + "download", null, "archive", + ArchiveFormat.TGZ)); + } } /** Scheme used to download. */ @@ -56,4 +70,9 @@ public Set<DownloadCommand> getDownloadCommands() { return downloadCommands; } + + /** Archive formats for downloading. */ + public Set<ArchiveFormat> getArchiveFormats() { + return archiveFormats; + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GcConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GcConfig.java index 54096bb..572b56e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GcConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GcConfig.java
@@ -23,14 +23,19 @@ @Singleton public class GcConfig { private final ScheduleConfig scheduleConfig; + private final boolean aggressive; @Inject GcConfig(@GerritServerConfig Config cfg) { scheduleConfig = new ScheduleConfig(cfg, ConfigConstants.CONFIG_GC_SECTION); + aggressive = cfg.getBoolean(ConfigConstants.CONFIG_GC_SECTION, "aggressive", false); } public ScheduleConfig getScheduleConfig() { return scheduleConfig; } + public boolean isAggressive() { + return aggressive; + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java index 7bdccaa..8e9d7e8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -22,6 +22,7 @@ import com.google.gerrit.extensions.config.CapabilityDefinition; import com.google.gerrit.extensions.config.DownloadCommand; import com.google.gerrit.extensions.config.DownloadScheme; +import com.google.gerrit.extensions.events.GarbageCollectorListener; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; import com.google.gerrit.extensions.events.HeadUpdatedListener; import com.google.gerrit.extensions.events.LifecycleListener; @@ -38,7 +39,6 @@ import com.google.gerrit.extensions.webui.PatchSetWebLink; import com.google.gerrit.extensions.webui.ProjectWebLink; import com.google.gerrit.extensions.webui.TopMenu; -import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.rules.PrologModule; import com.google.gerrit.rules.RulesCache; import com.google.gerrit.server.AnonymousUser; @@ -63,8 +63,6 @@ import com.google.gerrit.server.account.GroupIncludeCacheImpl; import com.google.gerrit.server.account.GroupInfoCacheFactory; import com.google.gerrit.server.account.GroupMembers; -import com.google.gerrit.server.account.PerformCreateGroup; -import com.google.gerrit.server.account.PerformRenameGroup; import com.google.gerrit.server.auth.AuthBackend; import com.google.gerrit.server.auth.UniversalAuthBackend; import com.google.gerrit.server.avatar.AvatarProvider; @@ -113,7 +111,6 @@ import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.CommentLinkInfo; import com.google.gerrit.server.project.CommentLinkProvider; -import com.google.gerrit.server.project.PerformCreateProject; import com.google.gerrit.server.project.PermissionCollection; import com.google.gerrit.server.project.ProjectCacheImpl; import com.google.gerrit.server.project.ProjectControl; @@ -125,6 +122,7 @@ import com.google.gerrit.server.ssh.SshAddressesModule; import com.google.gerrit.server.tools.ToolsCatalog; import com.google.gerrit.server.util.IdGenerator; +import com.google.gerrit.server.util.SubmoduleSectionParser; import com.google.gerrit.server.util.ThreadLocalRequestContext; import com.google.gerrit.server.validators.GroupCreationValidationListener; import com.google.gerrit.server.validators.HashtagValidationListener; @@ -139,7 +137,6 @@ import org.eclipse.jgit.transport.PreUploadHook; import java.util.List; -import java.util.Set; /** Starts global state with standard dependencies. */ @@ -195,21 +192,17 @@ factory(MergeFailSender.Factory.class); factory(MergeUtil.Factory.class); factory(PatchScriptFactory.Factory.class); - factory(PerformCreateGroup.Factory.class); - factory(PerformRenameGroup.Factory.class); factory(PluginUser.Factory.class); factory(ProjectNode.Factory.class); factory(ProjectState.Factory.class); factory(RegisterNewEmailSender.Factory.class); factory(ReplacePatchSetSender.Factory.class); - factory(PerformCreateProject.Factory.class); bind(PermissionCollection.Factory.class); bind(AccountVisibility.class) .toProvider(AccountVisibilityProvider.class) .in(SINGLETON); - bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) - .annotatedWith(ProjectOwnerGroups.class) - .toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON); + factory(ProjectOwnerGroupsProvider.Factory.class); + bind(RepositoryConfig.class); bind(AuthBackend.class).to(UniversalAuthBackend.class).in(SINGLETON); DynamicSet.setOf(binder(), AuthBackend.class); @@ -261,6 +254,7 @@ DynamicSet.setOf(binder(), PreUploadHook.class); DynamicSet.setOf(binder(), NewProjectCreatedListener.class); DynamicSet.setOf(binder(), ProjectDeletedListener.class); + DynamicSet.setOf(binder(), GarbageCollectorListener.class); DynamicSet.setOf(binder(), HeadUpdatedListener.class); DynamicSet.setOf(binder(), UsageDataPublishedListener.class); DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class); @@ -297,6 +291,7 @@ factory(MergeValidators.Factory.class); factory(ProjectConfigValidator.Factory.class); factory(NotesBranchUtil.Factory.class); + factory(SubmoduleSectionParser.Factory.class); bind(AccountManager.class); bind(ChangeUserName.CurrentUser.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigModule.java index 89331fd..281600d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigModule.java
@@ -29,12 +29,12 @@ import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; /** Creates {@link GerritServerConfig}. */ public class GerritServerConfigModule extends AbstractModule { - public static String getSecureStoreClassName(final File sitePath) { + public static String getSecureStoreClassName(final Path sitePath) { if (sitePath != null) { return getSecureStoreFromGerritConfig(sitePath); } @@ -43,17 +43,17 @@ return nullToDefault(secureStoreProperty); } - private static String getSecureStoreFromGerritConfig(final File sitePath) { + private static String getSecureStoreFromGerritConfig(final Path sitePath) { AbstractModule m = new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath); bind(SitePaths.class); } }; Injector injector = Guice.createInjector(m); SitePaths site = injector.getInstance(SitePaths.class); - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); if (!cfg.getFile().exists()) { return DefaultSecureStore.class.getName(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java index aa699c5..4b1236b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
@@ -44,10 +44,11 @@ @Override public Config get() { - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = + new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); if (!cfg.getFile().exists()) { - log.info("No " + site.gerrit_config.getAbsolutePath() + log.info("No " + site.gerrit_config.toAbsolutePath() + "; assuming defaults"); return new GerritConfig(cfg, secureStore); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java new file mode 100644 index 0000000..fe81dfd --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
@@ -0,0 +1,176 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.config; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import com.google.gerrit.extensions.config.DownloadCommand; +import com.google.gerrit.extensions.config.DownloadScheme; +import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AuthType; +import com.google.gerrit.server.account.Realm; +import com.google.gerrit.server.change.ArchiveFormat; +import com.google.inject.Inject; + +import org.eclipse.jgit.lib.Config; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class GetServerInfo implements RestReadView<ConfigResource> { + private final Config config; + private final AuthConfig authConfig; + private final Realm realm; + private final DownloadConfig downloadConfig; + private final DynamicMap<DownloadScheme> downloadSchemes; + private final DynamicMap<DownloadCommand> downloadCommands; + private final AllProjectsName allProjectsName; + private final AllUsersName allUsersName; + + @Inject + public GetServerInfo( + @GerritServerConfig Config config, + AuthConfig authConfig, + Realm realm, + DownloadConfig downloadConfig, + DynamicMap<DownloadScheme> downloadSchemes, + DynamicMap<DownloadCommand> downloadCommands, + AllProjectsName allProjectsName, + AllUsersName allUsersName) { + this.config = config; + this.authConfig = authConfig; + this.realm = realm; + this.downloadConfig = downloadConfig; + this.downloadSchemes = downloadSchemes; + this.downloadCommands = downloadCommands; + this.allProjectsName = allProjectsName; + this.allUsersName = allUsersName; + } + + @Override + public ServerInfo apply(ConfigResource rsrc) throws MalformedURLException { + ServerInfo info = new ServerInfo(); + info.auth = new AuthInfo(authConfig, realm); + info.contactStore = getContactStoreInfo(); + info.download = + new DownloadInfo(downloadConfig, downloadSchemes, downloadCommands); + info.gerrit = new GerritInfo(allProjectsName, allUsersName); + return info; + } + + private ContactStoreInfo getContactStoreInfo() { + String url = config.getString("contactstore", null, "url"); + if (url == null) { + return null; + } + + ContactStoreInfo contactStore = new ContactStoreInfo(); + contactStore.url = url; + return contactStore; + } + + private static Boolean toBoolean(boolean v) { + return v ? v : null; + } + + public static class ServerInfo { + public AuthInfo auth; + public ContactStoreInfo contactStore; + public DownloadInfo download; + public GerritInfo gerrit; + } + + public static class AuthInfo { + public AuthType authType; + public Boolean useContributorAgreements; + public List<Account.FieldName> editableAccountFields; + + public AuthInfo(AuthConfig cfg, Realm realm) { + authType = cfg.getAuthType(); + useContributorAgreements = toBoolean(cfg.isUseContributorAgreements()); + editableAccountFields = new ArrayList<>(realm.getEditableFields()); + } + } + + public static class ContactStoreInfo { + public String url; + } + + public static class DownloadInfo { + public Map<String, DownloadSchemeInfo> schemes; + public List<String> archives; + + public DownloadInfo(DownloadConfig downloadConfig, + DynamicMap<DownloadScheme> downloadSchemes, + DynamicMap<DownloadCommand> downloadCommands) { + schemes = new HashMap<>(); + for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) { + DownloadScheme scheme = e.getProvider().get(); + if (scheme.isEnabled() && scheme.getUrl("${project}") != null) { + schemes.put(e.getExportName(), + new DownloadSchemeInfo(scheme, downloadCommands)); + } + } + archives = + Lists.transform(new ArrayList<>(downloadConfig.getArchiveFormats()), + new Function<ArchiveFormat, String>() { + @Override + public String apply(ArchiveFormat archiveFormat) { + return archiveFormat.name().toLowerCase(Locale.US); + } + }); + } + } + + public static class DownloadSchemeInfo { + public String url; + public Boolean isAuthRequired; + public Boolean isAuthSupported; + public Map<String, String> commands; + + public DownloadSchemeInfo(DownloadScheme scheme, + DynamicMap<DownloadCommand> downloadCommands) { + url = scheme.getUrl("${project}"); + isAuthRequired = toBoolean(scheme.isAuthRequired()); + isAuthSupported = toBoolean(scheme.isAuthSupported()); + + commands = new HashMap<>(); + for (DynamicMap.Entry<DownloadCommand> e : downloadCommands) { + String commandName = e.getExportName(); + DownloadCommand command = e.getProvider().get(); + String c = command.getCommand(scheme, "${project}", "${ref}"); + if (c != null) { + commands.put(commandName, c); + } + } + } + } + + public static class GerritInfo { + public String allProjects; + public String allUsers; + + public GerritInfo(AllProjectsName allProjectsName, AllUsersName allUsersName) { + allProjects = allProjectsName.get(); + allUsers = allUsersName.get(); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java index 9aa8590..2d19f65 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
@@ -24,7 +24,6 @@ import org.eclipse.jgit.internal.storage.file.WindowCacheStatAccessor; import org.kohsuke.args4j.Option; -import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; @@ -33,6 +32,8 @@ import java.lang.management.ThreadMXBean; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -43,7 +44,7 @@ public class GetSummary implements RestReadView<ConfigResource> { private final WorkQueue workQueue; - private final File sitePath; + private final Path sitePath; @Option(name = "--gc", usage = "perform Java GC before retrieving memory stats") private boolean gc; @@ -62,7 +63,7 @@ } @Inject - public GetSummary(WorkQueue workQueue, @SitePath File sitePath) { + public GetSummary(WorkQueue workQueue, @SitePath Path sitePath) { this.workQueue = workQueue; this.sitePath = sitePath; } @@ -88,7 +89,9 @@ private TaskSummaryInfo getTaskSummary() { Collection<Task<?>> pending = workQueue.getTasks(); int tasksTotal = pending.size(); - int tasksRunning = 0, tasksReady = 0, tasksSleeping = 0; + int tasksRunning = 0; + int tasksReady = 0; + int tasksSleeping = 0; for (Task<?> task : pending) { switch (task.getState()) { case RUNNING: tasksRunning++; break; @@ -186,7 +189,8 @@ } catch (UnknownHostException e) { } - jvmSummary.currentWorkingDirectory = path(new File(".").getAbsoluteFile().getParentFile()); + jvmSummary.currentWorkingDirectory = + path(Paths.get(".").toAbsolutePath().getParent()); jvmSummary.site = path(sitePath); return jvmSummary; } @@ -210,11 +214,11 @@ return String.format("%1$6.2f%2$s", value, suffix).trim(); } - private static String path(File file) { + private static String path(Path path) { try { - return file.getCanonicalPath(); + return path.toRealPath().normalize().toString(); } catch (IOException err) { - return file.getAbsolutePath(); + return path.toAbsolutePath().normalize().toString(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java index 5e2f71f..5dd2784 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
@@ -30,7 +30,8 @@ @GerritServerConfig Config config, ThreadLocalRequestContext threadContext, ServerRequestContext serverCtx) { - super(gb, config, threadContext, serverCtx, "receive", null, "allowGroup"); + super(gb, threadContext, serverCtx, config.getStringList("receive", null, + "allowGroup")); // If no group was set, default to "registered users" //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java index 79cfd88..545f48b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
@@ -29,7 +29,8 @@ @GerritServerConfig Config config, ThreadLocalRequestContext threadContext, ServerRequestContext serverCtx) { - super(gb, config, threadContext, serverCtx, "upload", null, "allowGroup"); + super(gb, threadContext, serverCtx, config.getStringList("upload", null, + "allowGroup")); // If no group was set, default to "registered users" and "anonymous" //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java index 5c3ec39..9e55a7f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -25,7 +25,6 @@ import com.google.inject.Inject; import com.google.inject.Provider; -import org.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,13 +39,10 @@ @Inject protected GroupSetProvider(GroupBackend groupBackend, - @GerritServerConfig Config config, ThreadLocalRequestContext threadContext, - ServerRequestContext serverCtx, String section, - String subsection, String name) { + ServerRequestContext serverCtx, String[] groupNames) { RequestContext ctx = threadContext.setContext(serverCtx); try { - String[] groupNames = config.getStringList(section, subsection, name); ImmutableSet.Builder<AccountGroup.UUID> builder = ImmutableSet.builder(); for (String n : groupNames) { GroupReference g = GroupBackends.findBestSuggestion(groupBackend, n);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java index df6d86b..006419b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java
@@ -41,7 +41,7 @@ private final DynamicMap<Cache<?, ?>> cacheMap; public static enum OutputFormat { - LIST, TEXT_LIST; + LIST, TEXT_LIST } @Option(name = "--format", usage = "output format") @@ -85,7 +85,7 @@ } public enum CacheType { - MEM, DISK; + MEM, DISK } public static class CacheInfo {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java index 64848ba..31fdc1e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
@@ -35,6 +35,7 @@ delete(TASK_KIND).to(DeleteTask.class); child(CONFIG_KIND, "top-menus").to(TopMenuCollection.class); get(CONFIG_KIND, "version").to(GetVersion.class); + get(CONFIG_KIND, "info").to(GetServerInfo.class); get(CONFIG_KIND, "preferences").to(GetPreferences.class); put(CONFIG_KIND, "preferences").to(SetPreferences.class); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java index 76f5323..a437085 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -35,6 +35,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; @Singleton @@ -61,7 +62,7 @@ this.projectStateFactory = projectStateFactory; this.pluginConfigs = Maps.newHashMap(); - this.cfgSnapshot = FileSnapshot.save(site.gerrit_config); + this.cfgSnapshot = FileSnapshot.save(site.gerrit_config.toFile()); this.cfg = cfgProvider.get(); } @@ -103,8 +104,9 @@ * @return the plugin configuration from the 'gerrit.config' file */ public PluginConfig getFromGerritConfig(String pluginName, boolean refresh) { - if (refresh && cfgSnapshot.isModified(site.gerrit_config)) { - cfgSnapshot = FileSnapshot.save(site.gerrit_config); + File configFile = site.gerrit_config.toFile(); + if (refresh && cfgSnapshot.isModified(configFile)) { + cfgSnapshot = FileSnapshot.save(configFile); cfg = cfgProvider.get(); } return new PluginConfig(pluginName, cfg); @@ -237,33 +239,35 @@ /** * Returns the configuration for the specified plugin that is stored in the - * plugin configuration file 'etc/<plugin-name>.config'. + * plugin configuration file '{@code etc/<plugin-name>.config}'. * * The plugin configuration is only loaded once and is then cached. * * @param pluginName the name of the plugin for which the configuration should * be returned - * @return the plugin configuration from the 'etc/<plugin-name>.config' file + * @return the plugin configuration from the + * '{@code etc/<plugin-name>.config}' file */ public synchronized Config getGlobalPluginConfig(String pluginName) { if (pluginConfigs.containsKey(pluginName)) { return pluginConfigs.get(pluginName); } - File pluginConfigFile = new File(site.etc_dir, pluginName + ".config"); - FileBasedConfig cfg = new FileBasedConfig(pluginConfigFile, FS.DETECTED); + Path pluginConfigFile = site.etc_dir.resolve(pluginName + ".config"); + FileBasedConfig cfg = + new FileBasedConfig(pluginConfigFile.toFile(), FS.DETECTED); pluginConfigs.put(pluginName, cfg); if (!cfg.getFile().exists()) { - log.info("No " + pluginConfigFile.getAbsolutePath() + "; assuming defaults"); + log.info("No " + pluginConfigFile.toAbsolutePath() + "; assuming defaults"); return cfg; } try { cfg.load(); } catch (IOException e) { - log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e); + log.warn("Failed to load " + pluginConfigFile.toAbsolutePath(), e); } catch (ConfigInvalidException e) { - log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e); + log.warn("Failed to load " + pluginConfigFile.toAbsolutePath(), e); } return cfg; @@ -271,15 +275,15 @@ /** * Returns the configuration for the specified plugin that is stored in the - * '<plugin-name>.config' file in the 'refs/meta/config' branch of the - * specified project. + * '{@code <plugin-name>.config}' file in the 'refs/meta/config' branch of + * the specified project. * * @param projectName the name of the project for which the plugin * configuration should be returned * @param pluginName the name of the plugin for which the configuration should * be returned - * @return the plugin configuration from the '<plugin-name>.config' file of - * the specified project + * @return the plugin configuration from the '{@code <plugin-name>.config}' + * file of the specified project * @throws NoSuchProjectException thrown if the specified project does not * exist */ @@ -290,15 +294,15 @@ /** * Returns the configuration for the specified plugin that is stored in the - * '<plugin-name>.config' file in the 'refs/meta/config' branch of the - * specified project. + * '{@code <plugin-name>.config}' file in the 'refs/meta/config' branch of + * the specified project. * * @param projectState the project for which the plugin configuration should * be returned * @param pluginName the name of the plugin for which the configuration should * be returned - * @return the plugin configuration from the '<plugin-name>.config' file of - * the specified project + * @return the plugin configuration from the '{@code <plugin-name>.config}' + * file of the specified project */ public Config getProjectPluginConfig(ProjectState projectState, String pluginName) { @@ -307,10 +311,10 @@ /** * Returns the configuration for the specified plugin that is stored in the - * '<plugin-name>.config' file in the 'refs/meta/config' branch of the - * specified project. Parameters which are not set in the - * '<plugin-name>.config' of this project are inherited from the parent - * project's '<plugin-name>.config' files. + * '{@code <plugin-name>.config}' file in the 'refs/meta/config' branch of + * the specified project. Parameters which are not set in the + * '{@code <plugin-name>.config}' of this project are inherited from the + * parent project's '{@code <plugin-name>.config}' files. * * E.g.: child project: [mySection "mySubsection"] myKey = childValue * @@ -324,9 +328,9 @@ * configuration should be returned * @param pluginName the name of the plugin for which the configuration should * be returned - * @return the plugin configuration from the '<plugin-name>.config' file of - * the specified project with inheriting non-set parameters from the - * parent projects + * @return the plugin configuration from the '{@code <plugin-name>.config}' + * file of the specified project with inheriting non-set parameters + * from the parent projects * @throws NoSuchProjectException thrown if the specified project does not * exist */ @@ -337,10 +341,10 @@ /** * Returns the configuration for the specified plugin that is stored in the - * '<plugin-name>.config' file in the 'refs/meta/config' branch of the - * specified project. Parameters which are not set in the - * '<plugin-name>.config' of this project are inherited from the parent - * project's '<plugin-name>.config' files. + * '{@code <plugin-name>.config}' file in the 'refs/meta/config' branch of + * the specified project. Parameters which are not set in the + * '{@code <plugin-name>.config}' of this project are inherited from the + * parent project's '{@code <plugin-name>.config}' files. * * E.g.: child project: [mySection "mySubsection"] myKey = childValue * @@ -354,9 +358,9 @@ * be returned * @param pluginName the name of the plugin for which the configuration should * be returned - * @return the plugin configuration from the '<plugin-name>.config' file of - * the specified project with inheriting non-set parameters from the - * parent projects + * @return the plugin configuration from the '{@code <plugin-name>.config}' + * file of the specified project with inheriting non-set parameters + * from the parent projects */ public Config getProjectPluginConfigWithInheritance(ProjectState projectState, String pluginName) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java index 7302ea1..2bbb731 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java
@@ -52,7 +52,7 @@ } public static enum Operation { - FLUSH_ALL, FLUSH; + FLUSH_ALL, FLUSH } private final DynamicMap<Cache<?, ?>> cacheMap;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroups.java deleted file mode 100644 index 876c51f..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroups.java +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.config; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.inject.BindingAnnotation; - -import java.lang.annotation.Retention; - -/** - * Marker on a {@code Set<AccountGroup.Id>} for the configured groups which - * should become owners of a created project. - */ -@Retention(RUNTIME) -@BindingAnnotation -public @interface ProjectOwnerGroups { -} \ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java index 0189de3..23615d6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
@@ -14,30 +14,37 @@ package com.google.gerrit.server.config; +import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.account.GroupBackend; import com.google.gerrit.server.util.ServerRequestContext; import com.google.gerrit.server.util.ThreadLocalRequestContext; -import com.google.inject.Inject; - -import org.eclipse.jgit.lib.Config; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; /** * Provider of the group(s) which should become owners of a newly created - * project. Currently only supports {@code ownerGroup} declarations in the - * {@code "*"} repository, like so: + * project. The only matching patterns supported are exact match or wildcard + * matching which can be specified by ending the name with a {@code *}. * * <pre> * [repository "*"] * ownerGroup = Registered Users * ownerGroup = Administrators + * [repository "project/*"] + * ownerGroup = Administrators * </pre> */ public class ProjectOwnerGroupsProvider extends GroupSetProvider { - @Inject + + public interface Factory { + public ProjectOwnerGroupsProvider create(Project.NameKey project); + } + + @AssistedInject public ProjectOwnerGroupsProvider(GroupBackend gb, - @GerritServerConfig final Config config, - ThreadLocalRequestContext context, - ServerRequestContext serverCtx) { - super(gb, config, context, serverCtx, "repository", "*", "ownerGroup"); + ThreadLocalRequestContext context, ServerRequestContext serverCtx, + RepositoryConfig repositoryCfg, + @Assisted Project.NameKey project) { + super(gb, context, serverCtx, repositoryCfg.getOwnerGroups(project)); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/RepositoryConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/RepositoryConfig.java new file mode 100644 index 0000000..c34b8a6 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/RepositoryConfig.java
@@ -0,0 +1,85 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.config; + +import com.google.gerrit.extensions.client.SubmitType; +import com.google.gerrit.reviewdb.client.Project; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.jgit.lib.Config; + +@Singleton +public class RepositoryConfig { + + static final String SECTION_NAME = "repository"; + static final String OWNER_GROUP_NAME = "ownerGroup"; + static final String DEFAULT_SUBMIT_TYPE_NAME = "defaultSubmitType"; + + private final Config cfg; + + @Inject + public RepositoryConfig(@GerritServerConfig Config cfg) { + this.cfg = cfg; + } + + public SubmitType getDefaultSubmitType(Project.NameKey project) { + return cfg.getEnum(SECTION_NAME, findSubSection(project.get()), + DEFAULT_SUBMIT_TYPE_NAME, SubmitType.MERGE_IF_NECESSARY); + } + + public String[] getOwnerGroups(Project.NameKey project) { + return cfg.getStringList(SECTION_NAME, findSubSection(project.get()), + OWNER_GROUP_NAME); + } + + /** + * Find the subSection to get repository configuration from. + * <p> + * SubSection can use the * pattern so if project name matches more than one + * section, return the more precise one. E.g if the following subSections are + * defined: + * + * <pre> + * [repository "somePath/*"] + * name = value + * [repository "somePath/somePath/*"] + * name = value + * </pre> + * + * and this method is called with "somePath/somePath/someProject" as project + * name, it will return the subSection "somePath/somePath/*" + * + * @param project Name of the project + * @return the name of the subSection, null if none is found + */ + private String findSubSection(String project) { + String subSectionFound = null; + for (String subSection : cfg.getSubsections(SECTION_NAME)) { + if (isMatch(subSection, project) + && (subSectionFound == null || subSectionFound.length() < subSection + .length())) { + subSectionFound = subSection; + } + } + return subSectionFound; + } + + private boolean isMatch(String subSection, String project) { + return project.equals(subSection) + || (subSection.endsWith("*") && project.startsWith(subSection + .substring(0, subSection.length() - 1))); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java index fbff7c4..a6a3e53 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
@@ -14,12 +14,15 @@ package com.google.gerrit.server.config; +import com.google.common.collect.Iterables; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; /** Important paths within a {@link SitePath}. */ @Singleton @@ -28,88 +31,84 @@ public static final String HEADER_FILENAME = "GerritSiteHeader.html"; public static final String FOOTER_FILENAME = "GerritSiteFooter.html"; - public final File site_path; - public final File bin_dir; - public final File etc_dir; - public final File lib_dir; - public final File tmp_dir; - public final File logs_dir; - public final File plugins_dir; - public final File data_dir; - public final File mail_dir; - public final File hooks_dir; - public final File static_dir; - public final File themes_dir; - public final File index_dir; + public final Path site_path; + public final Path bin_dir; + public final Path etc_dir; + public final Path lib_dir; + public final Path tmp_dir; + public final Path logs_dir; + public final Path plugins_dir; + public final Path data_dir; + public final Path mail_dir; + public final Path hooks_dir; + public final Path static_dir; + public final Path themes_dir; + public final Path index_dir; - public final File gerrit_sh; - public final File gerrit_war; + public final Path gerrit_sh; + public final Path gerrit_war; - public final File gerrit_config; - public final File secure_config; - public final File contact_information_pub; + public final Path gerrit_config; + public final Path secure_config; + public final Path contact_information_pub; - public final File ssl_keystore; - public final File ssh_key; - public final File ssh_rsa; - public final File ssh_dsa; - public final File peer_keys; + public final Path ssl_keystore; + public final Path ssh_key; + public final Path ssh_rsa; + public final Path ssh_dsa; + public final Path peer_keys; - public final File site_css; - public final File site_header; - public final File site_footer; - public final File site_gitweb; + public final Path site_css; + public final Path site_header; + public final Path site_footer; + public final Path site_gitweb; /** {@code true} if {@link #site_path} has not been initialized. */ public final boolean isNew; @Inject - public SitePaths(final @SitePath File sitePath) throws FileNotFoundException { + public SitePaths(@SitePath Path sitePath) throws IOException { site_path = sitePath; + Path p = sitePath; - bin_dir = new File(site_path, "bin"); - etc_dir = new File(site_path, "etc"); - lib_dir = new File(site_path, "lib"); - tmp_dir = new File(site_path, "tmp"); - plugins_dir = new File(site_path, "plugins"); - data_dir = new File(site_path, "data"); - logs_dir = new File(site_path, "logs"); - mail_dir = new File(etc_dir, "mail"); - hooks_dir = new File(site_path, "hooks"); - static_dir = new File(site_path, "static"); - themes_dir = new File(site_path, "themes"); - index_dir = new File(site_path, "index"); + bin_dir = p.resolve("bin"); + etc_dir = p.resolve("etc"); + lib_dir = p.resolve("lib"); + tmp_dir = p.resolve("tmp"); + plugins_dir = p.resolve("plugins"); + data_dir = p.resolve("data"); + logs_dir = p.resolve("logs"); + mail_dir = etc_dir.resolve("mail"); + hooks_dir = p.resolve("hooks"); + static_dir = p.resolve("static"); + themes_dir = p.resolve("themes"); + index_dir = p.resolve("index"); - gerrit_sh = new File(bin_dir, "gerrit.sh"); - gerrit_war = new File(bin_dir, "gerrit.war"); + gerrit_sh = bin_dir.resolve("gerrit.sh"); + gerrit_war = bin_dir.resolve("gerrit.war"); - gerrit_config = new File(etc_dir, "gerrit.config"); - secure_config = new File(etc_dir, "secure.config"); - contact_information_pub = new File(etc_dir, "contact_information.pub"); + gerrit_config = etc_dir.resolve("gerrit.config"); + secure_config = etc_dir.resolve("secure.config"); + contact_information_pub = etc_dir.resolve("contact_information.pub"); - ssl_keystore = new File(etc_dir, "keystore"); - ssh_key = new File(etc_dir, "ssh_host_key"); - ssh_rsa = new File(etc_dir, "ssh_host_rsa_key"); - ssh_dsa = new File(etc_dir, "ssh_host_dsa_key"); - peer_keys = new File(etc_dir, "peer_keys"); + ssl_keystore = etc_dir.resolve("keystore"); + ssh_key = etc_dir.resolve("ssh_host_key"); + ssh_rsa = etc_dir.resolve("ssh_host_rsa_key"); + ssh_dsa = etc_dir.resolve("ssh_host_dsa_key"); + peer_keys = etc_dir.resolve("peer_keys"); - site_css = new File(etc_dir, CSS_FILENAME); - site_header = new File(etc_dir, HEADER_FILENAME); - site_footer = new File(etc_dir, FOOTER_FILENAME); - site_gitweb = new File(etc_dir, "gitweb_config.perl"); + site_css = etc_dir.resolve(CSS_FILENAME); + site_header = etc_dir.resolve(HEADER_FILENAME); + site_footer = etc_dir.resolve(FOOTER_FILENAME); + site_gitweb = etc_dir.resolve("gitweb_config.perl"); - if (site_path.exists()) { - final String[] contents = site_path.list(); - if (contents != null) { - isNew = contents.length == 0; - } else if (site_path.isDirectory()) { - throw new FileNotFoundException("Cannot access " + site_path); - } else { - throw new FileNotFoundException("Not a directory: " + site_path); - } - } else { + boolean isNew; + try (DirectoryStream<Path> files = Files.newDirectoryStream(site_path)) { + isNew = Iterables.isEmpty(files); + } catch (NoSuchFileException e) { isNew = true; } + this.isNew = isNew; } /** @@ -120,16 +119,13 @@ * @param path the path string to resolve. May be null. * @return the resolved path; null if {@code path} was null or empty. */ - public File resolve(final String path) { + public Path resolve(String path) { if (path != null && !path.isEmpty()) { - File loc = new File(path); - if (!loc.isAbsolute()) { - loc = new File(site_path, path); - } + Path loc = site_path.resolve(path).normalize(); try { - return loc.getCanonicalFile(); + return loc.toRealPath(); } catch (IOException e) { - return loc.getAbsoluteFile(); + return loc.toAbsolutePath(); } } return null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java index 6b195de..f6e08b8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java
@@ -28,11 +28,12 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.util.StringUtils; -import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.Security; /** Creates the {@link ContactStore} based on the configuration. */ @@ -46,7 +47,7 @@ public ContactStore provideContactStore(@GerritServerConfig final Config config, final SitePaths site, final SchemaFactory<ReviewDb> schema, final ContactStoreConnection.Factory connFactory) { - final String url = config.getString("contactstore", null, "url"); + String url = config.getString("contactstore", null, "url"); if (StringUtils.isEmptyOrNull(url)) { return new NoContactStore(); } @@ -56,18 +57,18 @@ + " needed to encrypt contact information"); } - final URL storeUrl; + URL storeUrl; try { storeUrl = new URL(url); } catch (MalformedURLException e) { throw new ProvisionException("Invalid contactstore.url: " + url, e); } - final String storeAPPSEC = config.getString("contactstore", null, "appsec"); - final File pubkey = site.contact_information_pub; - if (!pubkey.exists()) { + String storeAPPSEC = config.getString("contactstore", null, "appsec"); + Path pubkey = site.contact_information_pub; + if (!Files.exists(pubkey)) { throw new ProvisionException("PGP public key file \"" - + pubkey.getAbsolutePath() + "\" not found"); + + pubkey.toAbsolutePath() + "\" not found"); } return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema, connFactory);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java index 4048748..fedc909 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
@@ -45,12 +45,12 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.sql.Timestamp; @@ -74,7 +74,7 @@ private final ContactStoreConnection.Factory connFactory; EncryptedContactStore(final URL storeUrl, final String storeAPPSEC, - final File pubKey, final SchemaFactory<ReviewDb> schema, + final Path pubKey, final SchemaFactory<ReviewDb> schema, final ContactStoreConnection.Factory connFactory) { this.storeUrl = storeUrl; this.storeAPPSEC = storeAPPSEC; @@ -106,8 +106,8 @@ return true; } - private static PGPPublicKeyRingCollection readPubRing(final File pub) { - try (InputStream fin = new FileInputStream(pub); + private static PGPPublicKeyRingCollection readPubRing(Path pub) { + try (InputStream fin = Files.newInputStream(pub); InputStream in = PGPUtil.getDecoderStream(fin)) { return new BcPGPPublicKeyRingCollection(in); } catch (IOException | PGPException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java index 67de914..70107c2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -389,7 +389,7 @@ private static ObjectId writeNewTree(TreeOperation op, RevWalk rw, ObjectInserter ins, RevCommit prevEdit, ObjectReader reader, String fileName, @Nullable String newFile, - final @Nullable ObjectId content) throws IOException { + @Nullable final ObjectId content) throws IOException { DirCache newTree = readTree(reader, prevEdit); DirCacheEditor dce = newTree.editor(); switch (op) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java index 0a65527..79c6593 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -183,8 +183,8 @@ checkArgument(pos > 0, "invalid edit ref: %s", ref.getName()); String psId = ref.getName().substring(pos + 1); return db.get().patchSets().get(new PatchSet.Id( - change.getId(), Integer.valueOf(psId))); - } catch (OrmException e) { + change.getId(), Integer.parseInt(psId))); + } catch (OrmException | NumberFormatException e) { throw new IOException(e); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java index 9a5ad82..f083b7a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
@@ -14,10 +14,9 @@ package com.google.gerrit.server.events; -import static org.eclipse.jgit.lib.Constants.R_HEADS; - import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.data.ChangeAttribute; public abstract class ChangeEvent extends RefEvent { @@ -34,7 +33,7 @@ @Override public String getRefName() { - return R_HEADS + change.branch; + return RefNames.fullName(change.branch); } public Change.Key getChangeKey() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java index f0c0bc1..4a61e3e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -151,7 +151,7 @@ ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName(); ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName(); ru.project = refName.getParentKey().get(); - ru.refName = refName.getShortName(); + ru.refName = refName.get(); return ru; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java index 908fd0a..9b37c38 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java
@@ -35,6 +35,7 @@ registerClass(new ReviewerAddedEvent()); registerClass(new PatchSetCreatedEvent()); registerClass(new TopicChangedEvent()); + registerClass(new ProjectCreatedEvent()); } /** Register an event.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java new file mode 100644 index 0000000..c1534df --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.events; + +import com.google.gerrit.reviewdb.client.Project; + +public class ProjectCreatedEvent extends ProjectEvent { + public String projectName; + public String headName; + + public ProjectCreatedEvent() { + super("project-created"); + } + + @Override + public Project.NameKey getProjectNameKey() { + return new Project.NameKey(projectName); + } + + public String getHeadName() { + return headName; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java index 5327448..7eef0ee 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -46,16 +46,22 @@ this.listeners = listeners; } - public void fire(Project.NameKey project, RefUpdate refUpdate) { + public void fire(Project.NameKey project, RefUpdate refUpdate, + ReceiveCommand.Type type) { fire(project, refUpdate.getName(), refUpdate.getOldObjectId(), - refUpdate.getNewObjectId()); + refUpdate.getNewObjectId(), type); } - public void fire(Project.NameKey project, String ref, - ObjectId oldObjectId, ObjectId newObjectId) { + public void fire(Project.NameKey project, RefUpdate refUpdate) { + fire(project, refUpdate.getName(), refUpdate.getOldObjectId(), + refUpdate.getNewObjectId(), ReceiveCommand.Type.UPDATE); + } + + public void fire(Project.NameKey project, String ref, ObjectId oldObjectId, + ObjectId newObjectId, ReceiveCommand.Type type) { ObjectId o = oldObjectId != null ? oldObjectId : ObjectId.zeroId(); ObjectId n = newObjectId != null ? newObjectId : ObjectId.zeroId(); - Event event = new Event(project, ref, o.name(), n.name()); + Event event = new Event(project, ref, o.name(), n.name(), type); for (GitReferenceUpdatedListener l : listeners) { try { l.onGitReferenceUpdated(event); @@ -65,10 +71,19 @@ } } + public void fire(Project.NameKey project, String ref, ObjectId oldObjectId, + ObjectId newObjectId) { + fire(project, ref, oldObjectId, newObjectId, ReceiveCommand.Type.UPDATE); + } + + public void fire(Project.NameKey project, ReceiveCommand cmd) { + fire(project, cmd.getRefName(), cmd.getOldId(), cmd.getNewId(), cmd.getType()); + } + public void fire(Project.NameKey project, BatchRefUpdate batchRefUpdate) { for (ReceiveCommand cmd : batchRefUpdate.getCommands()) { if (cmd.getResult() == ReceiveCommand.Result.OK) { - fire(project, cmd.getRefName(), cmd.getOldId(), cmd.getNewId()); + fire(project, cmd); } } } @@ -78,13 +93,16 @@ private final String ref; private final String oldObjectId; private final String newObjectId; + private final ReceiveCommand.Type type; Event(Project.NameKey project, String ref, - String oldObjectId, String newObjectId) { + String oldObjectId, String newObjectId, + ReceiveCommand.Type type) { this.projectName = project.get(); this.ref = ref; this.oldObjectId = oldObjectId; this.newObjectId = newObjectId; + this.type = type; } @Override @@ -108,6 +126,21 @@ } @Override + public boolean isCreate() { + return type == ReceiveCommand.Type.CREATE; + } + + @Override + public boolean isDelete() { + return type == ReceiveCommand.Type.DELETE; + } + + @Override + public boolean isNonFastForward() { + return type == ReceiveCommand.Type.UPDATE_NONFASTFORWARD; + } + + @Override public String toString() { return String.format("%s[%s,%s: %s -> %s]", getClass().getSimpleName(), projectName, ref, oldObjectId, newObjectId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java index c447d31..f044342 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java
@@ -15,8 +15,7 @@ package com.google.gerrit.server.git; import com.google.common.collect.ImmutableList; - -import org.eclipse.jgit.lib.Constants; +import com.google.gerrit.reviewdb.client.RefNames; import java.util.List; @@ -35,22 +34,14 @@ } else { ImmutableList.Builder<String> builder = ImmutableList.builder(); for (String b : order) { - builder.add(fullName(b)); + builder.add(RefNames.fullName(b)); } this.order = builder.build(); } } - private static String fullName(String branch) { - if (branch.startsWith(Constants.R_HEADS)) { - return branch; - } else { - return Constants.R_HEADS + branch; - } - } - public List<String> getMoreStable(String branch) { - int i = order.indexOf(fullName(branch)); + int i = order.indexOf(RefNames.fullName(branch)); if (0 <= i) { return order.subList(i + 1, order.size()); } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java index 24af14d..a915a79 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java
@@ -16,7 +16,11 @@ import com.google.common.collect.Sets; import com.google.gerrit.common.data.GarbageCollectionResult; +import com.google.gerrit.extensions.events.GarbageCollectorListener; +import com.google.gerrit.extensions.events.GarbageCollectorListener.Event; +import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.config.GcConfig; import com.google.inject.Inject; import org.eclipse.jgit.api.GarbageCollectCommand; @@ -46,15 +50,21 @@ private final GitRepositoryManager repoManager; private final GarbageCollectionQueue gcQueue; + private final GcConfig gcConfig; + private final DynamicSet<GarbageCollectorListener> listeners; public interface Factory { GarbageCollection create(); } @Inject - GarbageCollection(GitRepositoryManager repoManager, GarbageCollectionQueue gcQueue) { + GarbageCollection(GitRepositoryManager repoManager, + GarbageCollectionQueue gcQueue, GcConfig config, + DynamicSet<GarbageCollectorListener> listeners) { this.repoManager = repoManager; this.gcQueue = gcQueue; + this.gcConfig = config; + this.listeners = listeners; } public GarbageCollectionResult run(List<Project.NameKey> projectNames) { @@ -63,6 +73,11 @@ public GarbageCollectionResult run(List<Project.NameKey> projectNames, PrintWriter writer) { + return run(projectNames, gcConfig.isAggressive(), writer); + } + + public GarbageCollectionResult run(List<Project.NameKey> projectNames, + boolean aggressive, PrintWriter writer) { GarbageCollectionResult result = new GarbageCollectionResult(); Set<Project.NameKey> projectsToGc = gcQueue.addAll(projectNames); for (Project.NameKey projectName : Sets.difference( @@ -74,15 +89,17 @@ Repository repo = null; try { repo = repoManager.openRepository(p); - logGcConfiguration(p, repo); + logGcConfiguration(p, repo, aggressive); print(writer, "collecting garbage for \"" + p + "\":\n"); GarbageCollectCommand gc = Git.wrap(repo).gc(); + gc.setAggressive(aggressive); logGcInfo(p, "before:", gc.getStatistics()); gc.setProgressMonitor(writer != null ? new TextProgressMonitor(writer) : NullProgressMonitor.INSTANCE); Properties statistics = gc.call(); logGcInfo(p, "after: ", statistics); print(writer, "done.\n\n"); + fire(p, statistics); } catch (RepositoryNotFoundException e) { logGcError(writer, p, e); result.addError(new GarbageCollectionResult.Error( @@ -102,6 +119,27 @@ return result; } + private void fire(final Project.NameKey p, final Properties statistics) { + Event event = new GarbageCollectorListener.Event() { + @Override + public String getProjectName() { + return p.get(); + } + + @Override + public Properties getStatistics() { + return statistics; + } + }; + for (GarbageCollectorListener l : listeners) { + try { + l.onGarbageCollected(event); + } catch (RuntimeException e) { + log.warn("Failure in GarbageCollectorListener", e); + } + } + } + private static void logGcInfo(Project.NameKey projectName, String msg) { logGcInfo(projectName, msg, null); } @@ -123,9 +161,10 @@ } private static void logGcConfiguration(Project.NameKey projectName, - Repository repo) { + Repository repo, boolean aggressive) { StringBuilder b = new StringBuilder(); Config cfg = repo.getConfig(); + b.append("gc.aggressive=").append(aggressive).append("; "); b.append(formatConfigValues(cfg, ConfigConstants.CONFIG_GC_SECTION, null)); for (String subsection : cfg.getSubsections(ConfigConstants.CONFIG_GC_SECTION)) { b.append(formatConfigValues(cfg, ConfigConstants.CONFIG_GC_SECTION,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollectionLogFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollectionLogFile.java index 2b0d3e926..b2a4311 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollectionLogFile.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollectionLogFile.java
@@ -23,7 +23,7 @@ import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; -import java.io.File; +import java.nio.file.Path; public class GarbageCollectionLogFile implements LifecycleListener { @@ -43,7 +43,7 @@ LogManager.getLogger(GarbageCollection.LOG_NAME).removeAllAppenders(); } - private static void initLogSystem(File logdir) { + private static void initLogSystem(Path logdir) { Logger gcLogger = LogManager.getLogger(GarbageCollection.LOG_NAME); gcLogger.removeAllAppenders(); gcLogger.addAppender(SystemLog.createAppender(logdir,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java index 29948c2..d07572b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupList.java
@@ -18,18 +18,15 @@ import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup.UUID; -import java.io.BufferedReader; import java.io.IOException; -import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -public class GroupList { +public class GroupList extends TabFile { public static final String FILE_NAME = "groups"; private final Map<AccountGroup.UUID, GroupReference> byUUID; @@ -37,24 +34,14 @@ this.byUUID = byUUID; } - public static GroupList parse(String text, ValidationError.Sink errors) throws IOException { - Map<AccountGroup.UUID, GroupReference> groupsByUUID = new HashMap<>(); - - BufferedReader br = new BufferedReader(new StringReader(text)); - String s; - for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) { - if (s.isEmpty() || s.startsWith("#")) { - continue; - } - - int tab = s.indexOf('\t'); - if (tab < 0) { - errors.error(new ValidationError(FILE_NAME, lineNumber, "missing tab delimiter")); - continue; - } - - AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim()); - String name = s.substring(tab + 1).trim(); + public static GroupList parse(String text, ValidationError.Sink errors) + throws IOException { + List<Row> rows = parse(text, FILE_NAME, errors); + Map<AccountGroup.UUID, GroupReference> groupsByUUID = + new HashMap<>(rows.size()); + for(Row row : rows) { + AccountGroup.UUID uuid = new AccountGroup.UUID(row.left); + String name = row.right; GroupReference ref = new GroupReference(uuid, name); groupsByUUID.put(uuid, ref); @@ -90,49 +77,19 @@ byUUID.put(uuid, reference); } - private static String pad(int len, String src) { - if (len <= src.length()) { - return src; - } - - StringBuilder r = new StringBuilder(len); - r.append(src); - while (r.length() < len) { - r.append(' '); - } - return r.toString(); - } - - private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) { - ArrayList<T> r = new ArrayList<>(m); - Collections.sort(r); - return r; - } - public String asText() { if (byUUID.isEmpty()) { return null; } - final int uuidLen = 40; - StringBuilder buf = new StringBuilder(); - buf.append(pad(uuidLen, "# UUID")); - buf.append('\t'); - buf.append("Group Name"); - buf.append('\n'); - - buf.append('#'); - buf.append('\n'); - + List<Row> rows = new ArrayList<>(byUUID.size()); for (GroupReference g : sort(byUUID.values())) { if (g.getUUID() != null && g.getName() != null) { - buf.append(pad(uuidLen, g.getUUID().get())); - buf.append('\t'); - buf.append(g.getName()); - buf.append('\n'); + rows.add(new Row(g.getUUID().get(), g.getName())); } } - return buf.toString(); + + return asText("UUID", "Group Name", rows); } public void retainUUIDs(Collection<AccountGroup.UUID> toBeRetained) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/InsertException.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/InsertException.java index 575ad52..cf18de0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/InsertException.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/InsertException.java
@@ -1,16 +1,16 @@ -//Copyright (C) 2014 The Android Open Source Project +// Copyright (C) 2014 The Android Open Source Project // -//Licensed under the Apache License, Version 2.0 (the "License"); -//you may not use this file except in compliance with the License. -//You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -//http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -//Unless required by applicable law or agreed to in writing, software -//distributed under the License is distributed on an "AS IS" BASIS, -//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -//See the License for the specific language governing permissions and -//limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.google.gerrit.server.git;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index 633c3bb..717b393 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -51,7 +51,14 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; +import java.util.EnumSet; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.locks.Lock; @@ -128,8 +135,8 @@ } } - private final File basePath; - private final File noteDbPath; + private final Path basePath; + private final Path noteDbPath; private final Lock namesUpdateLock; private volatile SortedSet<Project.NameKey> names; @@ -153,7 +160,7 @@ } /** @return base directory under which all projects are stored. */ - public File getBasePath() { + public Path getBasePath() { return basePath; } @@ -163,12 +170,12 @@ return openRepository(basePath, name); } - private Repository openRepository(File path, Project.NameKey name) + private Repository openRepository(Path path, Project.NameKey name) throws RepositoryNotFoundException { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - File gitDir = new File(path, name.get()); + File gitDir = path.resolve(name.get()).toFile(); if (!names.contains(name)) { // The this.names list does not hold the project-name but it can still exist // on disk; for instance when the project has been created directly on the @@ -214,13 +221,13 @@ return repo; } - private Repository createRepository(File path, Project.NameKey name) + private Repository createRepository(Path path, Project.NameKey name) throws RepositoryNotFoundException, RepositoryCaseMismatchException { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - File dir = FileKey.resolve(new File(path, name.get()), FS.DETECTED); + File dir = FileKey.resolve(path.resolve(name.get()).toFile(), FS.DETECTED); FileKey loc; if (dir != null) { // Already exists on disk, use the repository we found. @@ -235,7 +242,7 @@ // of the repository name, so prefer the standard bare name. // String n = name.get() + Constants.DOT_GIT_EXT; - loc = FileKey.exact(new File(path, n), FS.DETECTED); + loc = FileKey.exact(path.resolve(n).toFile(), FS.DETECTED); } try { @@ -366,28 +373,25 @@ private boolean isUnreasonableName(final Project.NameKey nameKey) { final String name = nameKey.get(); - if (name.length() == 0) return true; // no empty paths - if (name.charAt(name.length() -1) == '/') return true; // no suffix - - if (name.indexOf('\\') >= 0) return true; // no windows/dos style paths - if (name.charAt(0) == '/') return true; // no absolute paths - if (new File(name).isAbsolute()) return true; // no absolute paths - - if (name.startsWith("../")) return true; // no "l../etc/passwd" - if (name.contains("/../")) return true; // no "foo/../etc/passwd" - if (name.contains("/./")) return true; // "foo/./foo" is insane to ask - if (name.contains("//")) return true; // windows UNC path can be "//..." - if (name.contains("?")) return true; // common unix wildcard - if (name.contains("%")) return true; // wildcard or string parameter - if (name.contains("*")) return true; // wildcard - if (name.contains(":")) return true; // Could be used for absolute paths in windows? - if (name.contains("<")) return true; // redirect input - if (name.contains(">")) return true; // redirect output - if (name.contains("|")) return true; // pipe - if (name.contains("$")) return true; // dollar sign - if (name.contains("\r")) return true; // carriage return - - return false; // is a reasonable name + return name.length() == 0 // no empty paths + || name.charAt(name.length() -1) == '/' // no suffix + || name.indexOf('\\') >= 0 // no windows/dos style paths + || name.charAt(0) == '/' // no absolute paths + || new File(name).isAbsolute() // no absolute paths + || name.startsWith("../") // no "l../etc/passwd" + || name.contains("/../") // no "foo/../etc/passwd" + || name.contains("/./") // "foo/./foo" is insane to ask + || name.contains("//") // windows UNC path can be "//..." + || name.contains(".git/") // no path segments that end with '.git' as "foo.git/bar" + || name.contains("?") // common unix wildcard + || name.contains("%") // wildcard or string parameter + || name.contains("*") // wildcard + || name.contains(":") // Could be used for absolute paths in windows? + || name.contains("<") // redirect input + || name.contains(">") // redirect output + || name.contains("|") // pipe + || name.contains("$") // dollar sign + || name.contains("\r"); // carriage return } @Override @@ -397,51 +401,59 @@ // scanning the filesystem. Don't rely on the cached names collection. namesUpdateLock.lock(); try { - SortedSet<Project.NameKey> n = new TreeSet<>(); - scanProjects(basePath, "", n); - names = Collections.unmodifiableSortedSet(n); - return n; + ProjectVisitor visitor = new ProjectVisitor(); + try { + Files.walkFileTree(basePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), + Integer.MAX_VALUE, visitor); + } catch (IOException e) { + log.error("Error walking repository tree " + basePath.toAbsolutePath(), + e); + } + return Collections.unmodifiableSortedSet(visitor.found); } finally { namesUpdateLock.unlock(); } } - private void scanProjects(final File dir, final String prefix, - final SortedSet<Project.NameKey> names) { - final File[] ls = dir.listFiles(); - if (ls == null) { - return; + private class ProjectVisitor extends SimpleFileVisitor<Path> { + private final SortedSet<Project.NameKey> found = new TreeSet<>(); + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) throws IOException { + if (!dir.equals(basePath) && isRepo(dir)) { + addProject(dir); + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; } - for (File f : ls) { - String fileName = f.getName(); - if (fileName.equals(Constants.DOT_GIT)) { - // Skip repositories named only `.git` - } else if (FileKey.isGitRepository(f, FS.DETECTED)) { - Project.NameKey nameKey = getProjectName(prefix, fileName); - if (isUnreasonableName(nameKey)) { - log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath()); - } else { - names.add(nameKey); - } + private boolean isRepo(Path p) { + String name = p.getFileName().toString(); + return !name.equals(Constants.DOT_GIT) + && name.endsWith(Constants.DOT_GIT_EXT); + } - } else if (f.isDirectory()) { - scanProjects(f, prefix + f.getName() + "/", names); + private void addProject(Path p) { + Project.NameKey nameKey = getProjectName(p); + if (isUnreasonableName(nameKey)) { + log.warn( + "Ignoring unreasonably named repository " + p.toAbsolutePath()); + } else { + found.add(nameKey); } } - } - private Project.NameKey getProjectName(final String prefix, - final String fileName) { - final String projectName; - if (fileName.endsWith(Constants.DOT_GIT_EXT)) { - int newLen = fileName.length() - Constants.DOT_GIT_EXT.length(); - projectName = prefix + fileName.substring(0, newLen); - - } else { - projectName = prefix + fileName; + private Project.NameKey getProjectName(Path p) { + String projectName = basePath.relativize(p).toString(); + if (File.separatorChar != '/') { + projectName = projectName.replace(File.separatorChar, '/'); + } + if (projectName.endsWith(Constants.DOT_GIT_EXT)) { + int newLen = projectName.length() - Constants.DOT_GIT_EXT.length(); + projectName = projectName.substring(0, newLen); + } + return new Project.NameKey(projectName); } - - return new Project.NameKey(projectName); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java index 05e864b..96207fa 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -24,7 +24,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.CheckedFuture; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.Nullable; import com.google.gerrit.common.TimeUtil; @@ -814,6 +813,19 @@ potentiallyStillSubmittable.add(commit); break; + case REVISION_GONE: + logDebug("Commit not found for change {}", c.getId()); + ChangeMessage msg = new ChangeMessage( + new ChangeMessage.Key( + c.getId(), + ChangeUtil.messageUUID(db)), + null, + TimeUtil.nowTs(), + c.currentPatchSetId()); + msg.setMessage("Failed to read commit for this patch set"); + sendMergeFail(commit.notes(), msg, false); + break; + default: setNew(commit, message(c, "Unspecified merge failure: " + s.name())); @@ -1032,7 +1044,7 @@ } try { - MergedSender cm = mergedSenderFactory.create(changeControl(c)); + MergedSender cm = mergedSenderFactory.create(c.getId()); if (from != null) { cm.setFrom(from.getAccountId()); } @@ -1174,12 +1186,7 @@ update.commit(); } - CheckedFuture<?, IOException> indexFuture; - if (change != null) { - indexFuture = indexer.indexAsync(change.getId()); - } else { - indexFuture = null; - } + indexer.index(db, change); final PatchSetApproval from = submitter; workQueue.getDefaultQueue() .submit(requestScopePropagator.wrap(new Runnable() { @@ -1199,7 +1206,7 @@ } try { - MergeFailSender cm = mergeFailSenderFactory.create(c); + MergeFailSender cm = mergeFailSenderFactory.create(c.getId()); if (from != null) { cm.setFrom(from.getAccountId()); } @@ -1217,14 +1224,6 @@ } })); - if (indexFuture != null) { - try { - indexFuture.checkedGet(); - } catch (IOException e) { - logError("Failed to index new change message", e); - } - } - if (submitter != null) { try { hooks.doMergeFailedHook(c,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeTip.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeTip.java index ba92651..27fb7f2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeTip.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeTip.java
@@ -15,6 +15,7 @@ package com.google.gerrit.server.git; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.Maps; import com.google.gerrit.common.Nullable; @@ -44,8 +45,8 @@ */ public MergeTip(@Nullable CodeReviewCommit initial, Collection<CodeReviewCommit> toMerge) { - checkArgument(toMerge != null && !toMerge.isEmpty(), - "toMerge may not be null or empty: %s", toMerge); + checkNotNull(toMerge, "toMerge may not be null"); + checkArgument(!toMerge.isEmpty(), "toMerge may not be empty"); this.mergeResults = Maps.newHashMap(); this.branchTip = initial; // Assume fast-forward merge until opposite is proven.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java index 295ad52..da38a58 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -25,13 +25,16 @@ import com.google.gerrit.common.data.LabelType; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.LabelId; +import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ProjectState; import com.google.gwtorm.server.OrmException; import com.google.inject.Provider; @@ -195,7 +198,9 @@ } } - public String createCherryPickCommitMessage(final CodeReviewCommit n) { + public String createCherryPickCommitMessage(RevCommit n, ChangeControl ctl, + PatchSet.Id psId) { + Change c = ctl.getChange(); final List<FooterLine> footers = n.getFooterLines(); final StringBuilder msgbuf = new StringBuilder(); msgbuf.append(n.getFullMessage()); @@ -215,16 +220,16 @@ msgbuf.append('\n'); } - if (!contains(footers, FooterConstants.CHANGE_ID, n.change().getKey().get())) { + if (!contains(footers, FooterConstants.CHANGE_ID, c.getKey().get())) { msgbuf.append(FooterConstants.CHANGE_ID.getName()); msgbuf.append(": "); - msgbuf.append(n.change().getKey().get()); + msgbuf.append(c.getKey().get()); msgbuf.append('\n'); } final String siteUrl = urlProvider.get(); if (siteUrl != null) { - final String url = siteUrl + n.getPatchsetId().getParentKey().get(); + final String url = siteUrl + c.getId().get(); if (!contains(footers, FooterConstants.REVIEWED_ON, url)) { msgbuf.append(FooterConstants.REVIEWED_ON.getName()); msgbuf.append(": "); @@ -235,7 +240,7 @@ PatchSetApproval submitAudit = null; - for (final PatchSetApproval a : safeGetApprovals(n)) { + for (final PatchSetApproval a : safeGetApprovals(ctl, psId)) { if (a.getValue() <= 0) { // Negative votes aren't counted. continue; @@ -301,6 +306,10 @@ return msgbuf.toString(); } + public String createCherryPickCommitMessage(final CodeReviewCommit n) { + return createCherryPickCommitMessage(n, n.getControl(), n.getPatchsetId()); + } + private static boolean isCodeReview(LabelId id) { return "Code-Review".equalsIgnoreCase(id.get()); } @@ -309,11 +318,12 @@ return "Verified".equalsIgnoreCase(id.get()); } - private Iterable<PatchSetApproval> safeGetApprovals(CodeReviewCommit n) { + private Iterable<PatchSetApproval> safeGetApprovals( + ChangeControl ctl, PatchSet.Id psId) { try { - return approvalsUtil.byPatchSet(db.get(), n.getControl(), n.getPatchsetId()); + return approvalsUtil.byPatchSet(db.get(), ctl, psId); } catch (OrmException e) { - log.error("Can't read approval records for " + n.getPatchsetId(), e); + log.error("Can't read approval records for " + psId, e); return Collections.emptyList(); } } @@ -667,7 +677,7 @@ } @Override - public void release() { + public void close() { } }); return (ThreeWayMerger) m;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java index c71c94f..840b167 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java
@@ -148,6 +148,7 @@ private final BatchRefUpdate batch; private final CommitBuilder commit; private boolean allowEmpty; + private boolean insertChangeId; @AssistedInject public MetaDataUpdate(GitReferenceUpdated gitRefUpdated, @@ -180,6 +181,10 @@ this.allowEmpty = allowEmpty; } + public void setInsertChangeId(boolean insertChangeId) { + this.insertChangeId = insertChangeId; + } + /** @return batch in which to run the update, or {@code null} for no batch. */ BatchRefUpdate getBatch() { return batch; @@ -202,6 +207,10 @@ return allowEmpty; } + boolean insertChangeId() { + return insertChangeId; + } + public CommitBuilder getCommitBuilder() { return commit; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java index 020c94a..99e61d6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -305,6 +305,10 @@ return notifySections.values(); } + public void putNotifyConfig(String name, NotifyConfig nc) { + notifySections.put(name, nc); + } + public Map<String, LabelType> getLabelSections() { return labelSections; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java new file mode 100644 index 0000000..0df866d --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java
@@ -0,0 +1,41 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class QueryList extends TabFile { + public static final String FILE_NAME = "queries"; + protected final Map<String, String> queriesByName; + + private QueryList(List<Row> queriesByName) { + this.queriesByName = toMap(queriesByName); + } + + public static QueryList parse(String text, ValidationError.Sink errors) + throws IOException { + return new QueryList(parse(text, FILE_NAME, errors)); + } + + public String getQuery(String name) { + return queriesByName.get(name); + } + + public String asText() { + return asText("Name", "Query", queriesByName); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java index f83bab6..3fb515a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -641,8 +641,7 @@ // We only fire gitRefUpdated for direct refs updates. // Events for change refs are fired when they are created. // - gitRefUpdated.fire(project.getNameKey(), c.getRefName(), - c.getOldId(), c.getNewId()); + gitRefUpdated.fire(project.getNameKey(), c); hooks.doRefUpdatedHook( new Branch.NameKey(project.getNameKey(), c.getRefName()), c.getOldId(), @@ -1729,18 +1728,15 @@ throws OrmException, IOException { Submit submit = submitProvider.get(); RevisionResource rsrc = new RevisionResource(changes.parse(changeCtl), ps); - Change c; + List<Change> changes; try { // Force submit even if submit rule evaluation fails. - c = submit.submit(rsrc, currentUser, true); + changes = submit.submit(rsrc, currentUser, true); } catch (ResourceConflictException e) { throw new IOException(e); } - if (c == null) { - addError("Submitting change " + changeCtl.getChange().getChangeId() - + " failed."); - } else { - addMessage(""); + addMessage(""); + for (Change c : changes) { mergeQueue.merge(c.getDest()); c = db.changes().get(c.getId()); switch (c.getStatus()) { @@ -2210,7 +2206,7 @@ if (cmd.getResult() == NOT_ATTEMPTED) { cmd.execute(rp); } - CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId()); + indexer.index(db, change); if (changeKind != ChangeKind.TRIVIAL_REBASE) { workQueue.getDefaultQueue() .submit(requestScopePropagator.wrap(new Runnable() { @@ -2218,7 +2214,7 @@ public void run() { try { ReplacePatchSetSender cm = - replacePatchSetFactory.create(change); + replacePatchSetFactory.create(change.getId()); cm.setFrom(me); cm.setPatchSet(newPatchSet, info); cm.setChangeMessage(msg); @@ -2239,7 +2235,6 @@ } })); } - f.checkedGet(); gitRefUpdated.fire(project.getNameKey(), newPatchSet.getRefName(), ObjectId.zeroId(), newCommit); @@ -2372,9 +2367,12 @@ walk.reset(); walk.sort(RevSort.NONE); try { - walk.markStart(walk.parseCommit(cmd.getNewId())); + RevObject parsedObject = walk.parseAny(cmd.getNewId()); + if (!(parsedObject instanceof RevCommit)) { + return; + } + walk.markStart((RevCommit)parsedObject); markHeadsAsUninteresting(walk, cmd.getRefName()); - Set<ObjectId> existing = changeRefsById().keySet(); for (RevCommit c; (c = walk.next()) != null;) { if (existing.contains(c)) { @@ -2598,12 +2596,13 @@ } private void sendMergedEmail(final ReplaceRequest result) { + final Change.Id id = result.change.getId(); workQueue.getDefaultQueue() .submit(requestScopePropagator.wrap(new Runnable() { @Override public void run() { try { - final MergedSender cm = mergedSenderFactory.create(result.changeCtl); + final MergedSender cm = mergedSenderFactory.create(id); cm.setFrom(currentUser.getAccountId()); cm.setPatchSet(result.newPatchSet, result.info); cm.send();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java index 7095552..b2d3632 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
@@ -154,9 +154,10 @@ RevCommit c; try { while ((c = rw.next()) != null && toInclude.size() < max) { - if (alreadySending.contains(c)) { - } else if (toInclude.contains(c)) { - } else if (c.getParentCount() > 1) { + if (alreadySending.contains(c) + || toInclude.contains(c) + || c.getParentCount() > 1) { + // Do nothing } else if (toInclude.size() < base) { toInclude.add(c); } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java index 25fbfb9..365000b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java
@@ -25,6 +25,7 @@ import org.eclipse.jgit.lib.Config; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -48,9 +49,12 @@ @Provides @Singleton @EmailReviewCommentsExecutor - public WorkQueue.Executor createEmailReviewCommentsExecutor( + public ExecutorService createEmailReviewCommentsExecutor( @GerritServerConfig Config config, WorkQueue queues) { int poolSize = config.getInt("sendemail", null, "threadPoolSize", 1); + if (poolSize == 0) { + return MoreExecutors.newDirectExecutorService(); + } return queues.createQueue(poolSize, "EmailReviewComments"); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java index 3cf9fed..0f66da4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -16,6 +16,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.Nullable; import com.google.gerrit.reviewdb.client.Account; @@ -61,6 +62,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -93,17 +95,25 @@ private final Set<Branch.NameKey> updatedSubscribers; private final Account account; private final ChangeHooks changeHooks; + private final SubmoduleSectionParser.Factory subSecParserFactory; @Inject - public SubmoduleOp(@Assisted final Branch.NameKey destBranch, - @Assisted RevCommit mergeTip, @Assisted RevWalk rw, - @CanonicalWebUrl @Nullable final Provider<String> urlProvider, - final SchemaFactory<ReviewDb> sf, @Assisted Repository db, - @Assisted Project destProject, @Assisted List<Change> submitted, - @Assisted final Map<Change.Id, CodeReviewCommit> commits, - @GerritPersonIdent final PersonIdent myIdent, - GitRepositoryManager repoManager, GitReferenceUpdated gitRefUpdated, - @Nullable @Assisted Account account, ChangeHooks changeHooks) { + public SubmoduleOp(@Assisted Branch.NameKey destBranch, + @Assisted RevCommit mergeTip, + @Assisted RevWalk rw, + @CanonicalWebUrl @Nullable Provider<String> urlProvider, + SchemaFactory<ReviewDb> sf, + @Assisted Repository db, + @Assisted Project destProject, + @Assisted List<Change> submitted, + @Assisted Map<Change.Id, + CodeReviewCommit> commits, + @GerritPersonIdent PersonIdent myIdent, + GitRepositoryManager repoManager, + GitReferenceUpdated gitRefUpdated, + @Nullable @Assisted Account account, + ChangeHooks changeHooks, + SubmoduleSectionParser.Factory subSecParserFactory) { this.destBranch = destBranch; this.mergeTip = mergeTip; this.rw = rw; @@ -118,6 +128,7 @@ this.gitRefUpdated = gitRefUpdated; this.account = account; this.changeHooks = changeHooks; + this.subSecParserFactory = subSecParserFactory; updatedSubscribers = new HashSet<>(); } @@ -128,7 +139,7 @@ updateSubmoduleSubscriptions(); updateSuperProjects(destBranch, rw, mergeTip.getId().toObjectId(), null); - } catch (OrmException e) { + } catch (OrmException | IOException e) { throw new SubmoduleException("Cannot open database", e); } finally { if (schema != null) { @@ -145,42 +156,47 @@ } try { - final TreeWalk tw = TreeWalk.forPath(db, GIT_MODULES, mergeTip.getTree()); - if (tw != null - && (FileMode.REGULAR_FILE.equals(tw.getRawMode(0)) || FileMode.EXECUTABLE_FILE - .equals(tw.getRawMode(0)))) { + Set<SubmoduleSubscription> oldSubscriptions = + Sets.newHashSet(schema.submoduleSubscriptions() + .bySuperProject(destBranch)); + Set<SubmoduleSubscription> newSubscriptions; + TreeWalk tw = TreeWalk.forPath(db, GIT_MODULES, mergeTip.getTree()); + if (tw != null + && (FileMode.REGULAR_FILE.equals(tw.getRawMode(0)) || + FileMode.EXECUTABLE_FILE.equals(tw.getRawMode(0)))) { BlobBasedConfig bbc = new BlobBasedConfig(null, db, mergeTip, GIT_MODULES); - final String thisServer = new URI(urlProvider.get()).getHost(); + String thisServer = new URI(urlProvider.get()).getHost(); - final Branch.NameKey target = + Branch.NameKey target = new Branch.NameKey(new Project.NameKey(destProject.getName()), destBranch.get()); - final Set<SubmoduleSubscription> oldSubscriptions = - new HashSet<>(schema.submoduleSubscriptions() - .bySuperProject(destBranch).toList()); - final List<SubmoduleSubscription> newSubscriptions = - new SubmoduleSectionParser(bbc, thisServer, target, repoManager) - .parseAllSections(); + newSubscriptions = subSecParserFactory.create(bbc, thisServer, target) + .parseAllSections(); + } else { + newSubscriptions = Collections.emptySet(); + } - final Set<SubmoduleSubscription> alreadySubscribeds = new HashSet<>(); - for (SubmoduleSubscription s : newSubscriptions) { - if (oldSubscriptions.contains(s)) { - alreadySubscribeds.add(s); - } + Set<SubmoduleSubscription> alreadySubscribeds = new HashSet<>(); + for (SubmoduleSubscription s : newSubscriptions) { + if (oldSubscriptions.contains(s)) { + alreadySubscribeds.add(s); } + } - oldSubscriptions.removeAll(newSubscriptions); - newSubscriptions.removeAll(alreadySubscribeds); + oldSubscriptions.removeAll(newSubscriptions); + newSubscriptions.removeAll(alreadySubscribeds); - if (!oldSubscriptions.isEmpty()) { - schema.submoduleSubscriptions().delete(oldSubscriptions); - } + if (!oldSubscriptions.isEmpty()) { + schema.submoduleSubscriptions().delete(oldSubscriptions); + } + if (!newSubscriptions.isEmpty()) { schema.submoduleSubscriptions().insert(newSubscriptions); } + } catch (OrmException e) { logAndThrowSubmoduleException( "Database problem at update of subscriptions table from " @@ -201,7 +217,8 @@ } private void updateSuperProjects(final Branch.NameKey updatedBranch, RevWalk myRw, - final ObjectId mergedCommit, final String msg) throws SubmoduleException { + final ObjectId mergedCommit, final String msg) throws SubmoduleException, + IOException { try { final List<SubmoduleSubscription> subscribers = schema.submoduleSubscriptions().bySubmodule(updatedBranch).toList(); @@ -227,6 +244,7 @@ && (c.getStatusCode() == CommitMergeStatus.CLEAN_MERGE || c.getStatusCode() == CommitMergeStatus.CLEAN_PICK || c.getStatusCode() == CommitMergeStatus.CLEAN_REBASE)) { + myRw.parseBody(c); sb.append("\n") .append(c.getFullMessage()); } @@ -278,8 +296,7 @@ throws SubmoduleException { PersonIdent author = null; - final StringBuilder msgbuf = new StringBuilder(); - msgbuf.append("Updated " + subscriber.getParentKey().get()); + final StringBuilder msgbuf = new StringBuilder("Updated git submodules\n"); Repository pdb = null; RevWalk recRw = null; @@ -351,6 +368,7 @@ commit.setCommitter(myIdent); commit.setMessage(msgbuf.toString()); oi.insert(commit); + oi.flush(); ObjectId commitId = oi.idFor(Constants.OBJ_COMMIT, commit.build());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java new file mode 100644 index 0000000..74d8f2d --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java
@@ -0,0 +1,129 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TabFile { + protected static class Row { + public String left; + public String right; + + public Row(String left, String right) { + this.left = left; + this.right = right; + } + } + + protected static List<Row> parse(String text, String filename, + ValidationError.Sink errors) throws IOException { + List<Row> rows = new ArrayList<>(); + BufferedReader br = new BufferedReader(new StringReader(text)); + String s; + for (int lineNumber = 1; (s = br.readLine()) != null; lineNumber++) { + if (s.isEmpty() || s.startsWith("#")) { + continue; + } + + int tab = s.indexOf('\t'); + if (tab < 0) { + errors.error(new ValidationError(filename, lineNumber, + "missing tab delimiter")); + continue; + } + + rows.add(new Row(s.substring(0, tab).trim(), + s.substring(tab + 1).trim())); + } + return rows; + } + + protected static Map<String, String> toMap(List<Row> rows) { + Map<String, String> map = new HashMap<>(rows.size()); + for (Row row : rows) { + map.put(row.left, row.right); + } + return map; + } + + protected static String asText(String left, String right, + Map<String, String> entries) { + if (entries.isEmpty()) { + return null; + } + + List<Row> rows = new ArrayList<>(entries.size()); + for (String key : sort(entries.keySet())) { + rows.add(new Row(key, entries.get(key))); + } + return asText(left, right, rows); + } + + protected static String asText(String left, String right, List<Row> rows) { + if (rows.isEmpty()) { + return null; + } + + left = "# " + left; + int leftLen = left.length(); + for (Row row : rows) { + leftLen = Math.max(leftLen, row.left.length()); + } + + StringBuilder buf = new StringBuilder(); + buf.append(pad(leftLen, left)); + buf.append('\t'); + buf.append(right); + buf.append('\n'); + + buf.append('#'); + buf.append('\n'); + + for (Row row : rows) { + buf.append(pad(leftLen, row.left)); + buf.append('\t'); + buf.append(row.right); + buf.append('\n'); + } + return buf.toString(); + } + + protected static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) { + ArrayList<T> r = new ArrayList<>(m); + Collections.sort(r); + return r; + } + + protected static String pad(int len, String src) { + if (len <= src.length()) { + return src; + } + + StringBuilder r = new StringBuilder(len); + r.append(src); + while (r.length() < len) { + r.append(' '); + } + return r.toString(); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java index b905f67..37df726 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -43,6 +43,7 @@ import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.ChangeIdUtil; import org.eclipse.jgit.util.RawParseUtils; import java.io.BufferedReader; @@ -271,6 +272,14 @@ commit.addParentId(src); } + if (update.insertChangeId()) { + ObjectId id = + ChangeIdUtil.computeChangeId(res, getRevision(), + commit.getAuthor(), commit.getCommitter(), + commit.getMessage()); + commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), id)); + } + src = inserter.insert(commit); srcTree = res; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java index ca9b992..4a8163b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
@@ -110,8 +110,8 @@ return defaultQueue; } - /** Create a new executor queue with one thread. */ - public Executor createQueue(final int poolsize, final String prefix) { + /** Create a new executor queue. */ + public Executor createQueue(int poolsize, String prefix) { final Executor r = new Executor(poolsize, prefix); r.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); r.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); @@ -303,11 +303,9 @@ final long delay = getDelay(TimeUnit.MILLISECONDS); if (delay <= 0) { return State.READY; - } else if (0 < delay) { + } else { return State.SLEEPING; } - - return State.OTHER; } public Date getStartTime() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java index acd32c7..dd981ad 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
@@ -20,7 +20,7 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy; -import com.google.gerrit.server.changedetail.RebaseChange; +import com.google.gerrit.server.change.RebaseChange; import com.google.gerrit.server.git.CodeReviewCommit; import com.google.gerrit.server.git.CommitMergeStatus; import com.google.gerrit.server.git.MergeConflictException; @@ -89,7 +89,7 @@ .getSubmitter(n).getAccountId()); PatchSet newPatchSet = rebaseChange.rebase(args.repo, args.rw, args.inserter, - n.getPatchsetId(), n.change(), uploader, + n.change(), n.getPatchsetId(), uploader, mergeTip.getCurrentTip(), args.mergeUtil, args.serverIdent.get(), false, ValidatePolicy.NONE);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java index ac65f8d..ffe351b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
@@ -20,7 +20,7 @@ import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.changedetail.RebaseChange; +import com.google.gerrit.server.change.RebaseChange; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.MergeException; import com.google.gerrit.server.git.MergeUtil;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java index d030a55..d8c3303 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -222,7 +222,7 @@ sb.append("ERROR: ").append(errMsg); if (c.getFullMessage().indexOf(changeId) >= 0) { - String lines[] = c.getFullMessage().trim().split("\n"); + String[] lines = c.getFullMessage().trim().split("\n"); String lastLine = lines.length > 0 ? lines[lines.length - 1] : ""; if (lastLine.indexOf(changeId) == -1) { @@ -387,7 +387,9 @@ final ProjectControl projectControl = refControl.getProjectControl(); if (projectControl.getProjectState().isUseSignedOffBy()) { - boolean sboAuthor = false, sboCommitter = false, sboMe = false; + boolean sboAuthor = false; + boolean sboCommitter = false; + boolean sboMe = false; for (final FooterLine footer : receiveEvent.commit.getFooterLines()) { if (footer.matches(FooterKey.SIGNED_OFF_BY)) { final String e = footer.getEmailAddress();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java index 9a3f02f..f3fb2a9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
@@ -20,6 +20,7 @@ import com.google.common.collect.Maps; import com.google.gerrit.audit.AuditService; import com.google.gerrit.common.data.GroupDescription; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; @@ -33,7 +34,6 @@ import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.account.GroupIncludeCache; import com.google.gerrit.server.group.AddIncludedGroups.Input; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java index 5c7fcbc..134d31c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -45,8 +45,11 @@ import com.google.inject.Provider; import com.google.inject.Singleton; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; @Singleton public class AddMembers implements RestModifyView<GroupResource, Input> { @@ -75,6 +78,7 @@ } } + private final Provider<IdentifiedUser> self; private final AccountManager accountManager; private final AuthType authType; private final AccountsCollection accounts; @@ -85,7 +89,8 @@ private final AuditService auditService; @Inject - AddMembers(AccountManager accountManager, + AddMembers(Provider<IdentifiedUser> self, + AccountManager accountManager, AuthConfig authConfig, AccountsCollection accounts, AccountResolver accountResolver, @@ -93,6 +98,7 @@ AccountLoader.Factory infoFactory, Provider<ReviewDb> db, AuditService auditService) { + this.self = self; this.accountManager = accountManager; this.auditService = auditService; this.authType = authConfig.getAuthType(); @@ -114,11 +120,8 @@ input = Input.init(input); GroupControl control = resource.getControl(); - Map<Account.Id, AccountGroupMember> newAccountGroupMembers = Maps.newHashMap(); - List<AccountInfo> result = Lists.newLinkedList(); - Account.Id me = ((IdentifiedUser) control.getCurrentUser()).getAccountId(); - AccountLoader loader = infoFactory.create(true); + Set<Account.Id> newMemberIds = new HashSet<>(); for (String nameOrEmail : input.members) { Account a = findAccount(nameOrEmail); if (!a.isActive()) { @@ -129,27 +132,11 @@ if (!control.canAddMember()) { throw new AuthException("Cannot add member: " + a.getFullName()); } - - if (!newAccountGroupMembers.containsKey(a.getId())) { - AccountGroupMember.Key key = - new AccountGroupMember.Key(a.getId(), internalGroup.getId()); - AccountGroupMember m = db.get().accountGroupMembers().get(key); - if (m == null) { - m = new AccountGroupMember(key); - newAccountGroupMembers.put(m.getAccountId(), m); - } - } - result.add(loader.get(a.getId())); + newMemberIds.add(a.getId()); } - auditService.dispatchAddAccountsToGroup(me, newAccountGroupMembers.values()); - db.get().accountGroupMembers().insert(newAccountGroupMembers.values()); - for (AccountGroupMember m : newAccountGroupMembers.values()) { - accountCache.evict(m.getAccountId()); - } - - loader.fill(); - return result; + addMembers(internalGroup.getId(), newMemberIds); + return toAccountInfoList(newMemberIds); } private Account findAccount(String nameOrEmail) throws AuthException, @@ -177,6 +164,29 @@ } } + public void addMembers(AccountGroup.Id groupId, + Collection<? extends Account.Id> newMemberIds) throws OrmException { + Map<Account.Id, AccountGroupMember> newAccountGroupMembers = Maps.newHashMap(); + for (Account.Id accId : newMemberIds) { + if (!newAccountGroupMembers.containsKey(accId)) { + AccountGroupMember.Key key = + new AccountGroupMember.Key(accId, groupId); + AccountGroupMember m = db.get().accountGroupMembers().get(key); + if (m == null) { + m = new AccountGroupMember(key); + newAccountGroupMembers.put(m.getAccountId(), m); + } + } + } + + auditService.dispatchAddAccountsToGroup(self.get().getAccountId(), + newAccountGroupMembers.values()); + db.get().accountGroupMembers().insert(newAccountGroupMembers.values()); + for (AccountGroupMember m : newAccountGroupMembers.values()) { + accountCache.evict(m.getAccountId()); + } + } + private Account createAccountByLdap(String user) { if (!user.matches(Account.USER_NAME_PATTERN)) { return null; @@ -192,6 +202,17 @@ } } + private List<AccountInfo> toAccountInfoList(Set<Account.Id> accountIds) + throws OrmException { + List<AccountInfo> result = Lists.newLinkedList(); + AccountLoader loader = infoFactory.create(true); + for (Account.Id accId : accountIds) { + result.add(loader.get(accId)); + } + loader.fill(); + return result; + } + static class PutMember implements RestModifyView<GroupResource, PutMember.Input> { static class Input { }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java index cb80702..0986584 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/CreateGroup.java
@@ -19,9 +19,9 @@ import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.data.GroupDescriptions; -import com.google.gerrit.common.errors.NameAlreadyUsedException; -import com.google.gerrit.common.errors.PermissionDeniedException; import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.api.groups.GroupInput; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; @@ -32,100 +32,102 @@ import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.reviewdb.client.AccountGroupName; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.CreateGroupArgs; -import com.google.gerrit.server.account.PerformCreateGroup; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.account.GroupUUID; import com.google.gerrit.server.config.GerritServerConfig; -import com.google.gerrit.server.group.CreateGroup.Input; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gerrit.server.validators.GroupCreationValidationListener; import com.google.gerrit.server.validators.ValidationException; +import com.google.gwtorm.server.OrmDuplicateKeyException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.assistedinject.Assisted; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; import java.util.Collections; @RequiresCapability(GlobalCapability.CREATE_GROUP) -public class CreateGroup implements RestModifyView<TopLevelResource, Input> { - public static class Input { - public String name; - public String description; - public Boolean visibleToAll; - public String ownerId; - } - +public class CreateGroup implements RestModifyView<TopLevelResource, GroupInput> { public static interface Factory { CreateGroup create(@Assisted String name); } private final Provider<IdentifiedUser> self; + private final PersonIdent serverIdent; + private final ReviewDb db; + private final GroupCache groupCache; private final GroupsCollection groups; - private final PerformCreateGroup.Factory op; private final GroupJson json; private final DynamicSet<GroupCreationValidationListener> groupCreationValidationListeners; + private final AddMembers addMembers; private final boolean defaultVisibleToAll; private final String name; @Inject - CreateGroup(Provider<IdentifiedUser> self, GroupsCollection groups, - PerformCreateGroup.Factory performCreateGroupFactory, GroupJson json, + CreateGroup( + Provider<IdentifiedUser> self, + @GerritPersonIdent PersonIdent serverIdent, + ReviewDb db, + GroupCache groupCache, + GroupsCollection groups, + GroupJson json, DynamicSet<GroupCreationValidationListener> groupCreationValidationListeners, - @GerritServerConfig Config cfg, @Assisted String name) { + AddMembers addMembers, + @GerritServerConfig Config cfg, + @Assisted String name) { this.self = self; + this.serverIdent = serverIdent; + this.db = db; + this.groupCache = groupCache; this.groups = groups; - this.op = performCreateGroupFactory; this.json = json; this.groupCreationValidationListeners = groupCreationValidationListeners; + this.addMembers = addMembers; this.defaultVisibleToAll = cfg.getBoolean("groups", "newGroupsVisibleToAll", false); this.name = name; } @Override - public GroupInfo apply(TopLevelResource resource, Input input) + public GroupInfo apply(TopLevelResource resource, GroupInput input) throws AuthException, BadRequestException, UnprocessableEntityException, ResourceConflictException, OrmException { if (input == null) { - input = new Input(); + input = new GroupInput(); } if (input.name != null && !name.equals(input.name)) { throw new BadRequestException("name must match URL"); } AccountGroup.Id ownerId = owner(input); - AccountGroup group; - try { - CreateGroupArgs args = new CreateGroupArgs(); - args.setGroupName(name); - args.groupDescription = Strings.emptyToNull(input.description); - args.visibleToAll = MoreObjects.firstNonNull(input.visibleToAll, - defaultVisibleToAll); - args.ownerGroupId = ownerId; - args.initialMembers = ownerId == null - ? Collections.singleton(self.get().getAccountId()) - : Collections.<Account.Id> emptySet(); + CreateGroupArgs args = new CreateGroupArgs(); + args.setGroupName(name); + args.groupDescription = Strings.emptyToNull(input.description); + args.visibleToAll = MoreObjects.firstNonNull(input.visibleToAll, + defaultVisibleToAll); + args.ownerGroupId = ownerId; + args.initialMembers = ownerId == null + ? Collections.singleton(self.get().getAccountId()) + : Collections.<Account.Id> emptySet(); - for (GroupCreationValidationListener l : groupCreationValidationListeners) { - try { - l.validateNewGroup(args); - } catch (ValidationException e) { - throw new ResourceConflictException(e.getMessage(), e); - } + for (GroupCreationValidationListener l : groupCreationValidationListeners) { + try { + l.validateNewGroup(args); + } catch (ValidationException e) { + throw new ResourceConflictException(e.getMessage(), e); } - - group = op.create(args).createGroup(); - } catch (PermissionDeniedException e) { - throw new AuthException(e.getMessage()); - } catch (NameAlreadyUsedException e) { - throw new ResourceConflictException(e.getMessage()); } - return json.format(GroupDescriptions.forAccountGroup(group)); + + return json.format(GroupDescriptions.forAccountGroup(createGroup(args))); } - private AccountGroup.Id owner(Input input) + private AccountGroup.Id owner(GroupInput input) throws UnprocessableEntityException { if (input.ownerId != null) { GroupDescription.Basic d = groups.parseInternal(Url.decode(input.ownerId)); @@ -133,4 +135,42 @@ } return null; } + + private AccountGroup createGroup(CreateGroupArgs createGroupArgs) + throws OrmException, ResourceConflictException { + AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId()); + AccountGroup.UUID uuid = + GroupUUID.make( + createGroupArgs.getGroupName(), + self.get().newCommitterIdent(serverIdent.getWhen(), + serverIdent.getTimeZone())); + AccountGroup group = + new AccountGroup(createGroupArgs.getGroup(), groupId, uuid); + group.setVisibleToAll(createGroupArgs.visibleToAll); + if (createGroupArgs.ownerGroupId != null) { + AccountGroup ownerGroup = groupCache.get(createGroupArgs.ownerGroupId); + if (ownerGroup != null) { + group.setOwnerGroupUUID(ownerGroup.getGroupUUID()); + } + } + if (createGroupArgs.groupDescription != null) { + group.setDescription(createGroupArgs.groupDescription); + } + AccountGroupName gn = new AccountGroupName(group); + // first insert the group name to validate that the group name hasn't + // already been used to create another group + try { + db.accountGroupNames().insert(Collections.singleton(gn)); + } catch (OrmDuplicateKeyException e) { + throw new ResourceConflictException("group '" + + createGroupArgs.getGroupName() + "' already exists"); + } + db.accountGroups().insert(Collections.singleton(group)); + + addMembers.addMembers(groupId, createGroupArgs.initialMembers); + + groupCache.onCreateGroup(createGroupArgs.getGroup()); + + return group; + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDescription.java index 8c804dd..6900b83 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDescription.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDescription.java
@@ -21,7 +21,7 @@ import com.google.inject.Singleton; @Singleton -class GetDescription implements RestReadView<GroupResource> { +public class GetDescription implements RestReadView<GroupResource> { @Override public String apply(GroupResource resource) throws MethodNotAllowedException { AccountGroup group = resource.toAccountGroup();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java index 936798d..615c862 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetDetail.java
@@ -14,9 +14,9 @@ package com.google.gerrit.server.group; -import com.google.gerrit.common.groups.ListGroupsOption; +import com.google.gerrit.extensions.client.ListGroupsOption; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Singleton;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java index 95042a2..03c6d6c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetGroup.java
@@ -14,14 +14,14 @@ package com.google.gerrit.server.group; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Singleton; @Singleton -class GetGroup implements RestReadView<GroupResource> { +public class GetGroup implements RestReadView<GroupResource> { private final GroupJson json; @Inject
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java index 5d3853e..dbc2e0c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetIncludedGroup.java
@@ -14,8 +14,8 @@ package com.google.gerrit.server.group; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Singleton;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java index 5d1ede0..7b55666 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java
@@ -14,6 +14,7 @@ package com.google.gerrit.server.group; +import com.google.gerrit.extensions.common.GroupOptionsInfo; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.inject.Singleton; @@ -22,6 +23,6 @@ @Override public GroupOptionsInfo apply(GroupResource resource) { - return new GroupOptionsInfo(resource.getGroup()); + return GroupJson.createOptions(resource.getGroup()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java index 5fc62c6..464be18 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOwner.java
@@ -15,12 +15,12 @@ package com.google.gerrit.server.group; import com.google.gerrit.common.errors.NoSuchGroupException; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.account.GroupControl; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Singleton;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java index 96b4234..8f339de 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupJson.java
@@ -14,14 +14,15 @@ package com.google.gerrit.server.group; -import static com.google.gerrit.common.groups.ListGroupsOption.INCLUDES; -import static com.google.gerrit.common.groups.ListGroupsOption.MEMBERS; +import static com.google.gerrit.extensions.client.ListGroupsOption.INCLUDES; +import static com.google.gerrit.extensions.client.ListGroupsOption.MEMBERS; import com.google.common.base.Strings; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.data.GroupDescriptions; -import com.google.gerrit.common.groups.ListGroupsOption; -import com.google.gerrit.extensions.common.AccountInfo; +import com.google.gerrit.extensions.client.ListGroupsOption; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.common.GroupOptionsInfo; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -33,9 +34,17 @@ import java.util.Collection; import java.util.EnumSet; -import java.util.List; public class GroupJson { + public static GroupOptionsInfo createOptions(GroupDescription.Basic group) { + GroupOptionsInfo options = new GroupOptionsInfo(); + AccountGroup ag = GroupDescriptions.toAccountGroup(group); + if (ag != null && ag.isVisibleToAll()) { + options.visibleToAll = true; + } + return options; + } + private final GroupBackend groupBackend; private final GroupControl.Factory groupControlFactory; private final Provider<ListMembers> listMembers; @@ -86,7 +95,7 @@ info.id = Url.encode(group.getGroupUUID().get()); info.name = Strings.emptyToNull(group.getName()); info.url = Strings.emptyToNull(group.getUrl()); - info.options = new GroupOptionsInfo(group); + info.options = createOptions(group); AccountGroup g = GroupDescriptions.toAccountGroup(group); if (g != null) { @@ -125,24 +134,4 @@ throw new IllegalStateException(e); } } - - public static class GroupInfo extends GroupBaseInfo { - public String url; - public GroupOptionsInfo options; - - // These fields are only supplied for internal groups. - public String description; - public Integer groupId; - public String owner; - public String ownerId; - - // These fields are only supplied for internal groups, but only if requested - public List<AccountInfo> members; - public List<GroupInfo> includes; - } - - public static class GroupBaseInfo { - public String id; - public String name; - } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java deleted file mode 100644 index 6be92d1..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java +++ /dev/null
@@ -1,32 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.group; - -import com.google.gerrit.common.data.GroupDescription; -import com.google.gerrit.common.data.GroupDescriptions; -import com.google.gerrit.reviewdb.client.AccountGroup; - -public class GroupOptionsInfo { - public Boolean visibleToAll; - - public GroupOptionsInfo(GroupDescription.Basic group) { - AccountGroup ag = GroupDescriptions.toAccountGroup(group); - visibleToAll = ag != null && ag.isVisibleToAll() ? true : null; - } - - public GroupOptionsInfo(AccountGroup group) { - visibleToAll = group.isVisibleToAll() ? true : null; - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java index 40d0420..bf5193f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -22,22 +22,20 @@ import com.google.gerrit.common.data.GroupDescriptions; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.common.groups.ListGroupsOption; +import com.google.gerrit.extensions.client.ListGroupsOption; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.GetGroups; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupComparator; import com.google.gerrit.server.account.GroupControl; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gerrit.server.project.ProjectControl; -import com.google.gson.reflect.TypeToken; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -51,53 +49,80 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.SortedMap; /** List groups visible to the calling user. */ public class ListGroups implements RestReadView<TopLevelResource> { protected final GroupCache groupCache; + private final List<ProjectControl> projects = new ArrayList<>(); + private final Set<AccountGroup.UUID> groupsToInspect = Sets.newHashSet(); private final GroupControl.Factory groupControlFactory; private final GroupControl.GenericFactory genericGroupControlFactory; private final Provider<IdentifiedUser> identifiedUser; private final IdentifiedUser.GenericFactory userFactory; private final Provider<GetGroups> accountGetGroups; private final GroupJson json; - private EnumSet<ListGroupsOption> options; + + private EnumSet<ListGroupsOption> options = + EnumSet.noneOf(ListGroupsOption.class); + private boolean visibleToAll; + private Account.Id user; + private boolean owned; + private int limit; + private int start; + private String matchSubstring; @Option(name = "--project", aliases = {"-p"}, usage = "projects for which the groups should be listed") - private final List<ProjectControl> projects = new ArrayList<>(); + public void addProject(ProjectControl project) { + projects.add(project); + } - @Option(name = "--visible-to-all", usage = "to list only groups that are visible to all registered users") - private boolean visibleToAll; + @Option(name = "--visible-to-all", + usage = "to list only groups that are visible to all registered users") + public void setVisibleToAll(boolean visibleToAll) { + this.visibleToAll = visibleToAll; + } @Option(name = "--user", aliases = {"-u"}, usage = "user for which the groups should be listed") - private Account.Id user; + public void setUser(Account.Id user) { + this.user = user; + } - @Option(name = "--owned", usage = "to list only groups that are owned by the specified user" - + " or by the calling user if no user was specifed") - private boolean owned; - - private Set<AccountGroup.UUID> groupsToInspect = Sets.newHashSet(); + @Option(name = "--owned", usage = "to list only groups that are owned by the" + + " specified user or by the calling user if no user was specifed") + public void setOwned(boolean owned) { + this.owned = owned; + } @Option(name = "-q", usage = "group to inspect") - void addGroup(final AccountGroup.UUID id) { + public void addGroup(AccountGroup.UUID id) { groupsToInspect.add(id); } - @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of groups to list") - private int limit; + @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", + usage = "maximum number of groups to list") + public void setLimit(int limit) { + this.limit = limit; + } - @Option(name = "--start", aliases = {"-S"}, metaVar = "CNT", usage = "number of groups to skip") - private int start; + @Option(name = "--start", aliases = {"-S"}, metaVar = "CNT", + usage = "number of groups to skip") + public void setStart(int start) { + this.start = start; + } - @Option(name = "--match", aliases = {"-m"}, metaVar = "MATCH", usage = "match group substring") - private String matchSubstring; + @Option(name = "--match", aliases = {"-m"}, metaVar = "MATCH", + usage = "match group substring") + public void setMatchSubstring(String matchSubstring) { + this.matchSubstring = matchSubstring; + } @Option(name = "-o", usage = "Output options per group") - public void addOption(ListGroupsOption o) { + void addOption(ListGroupsOption o) { options.add(o); } @@ -120,7 +145,10 @@ this.userFactory = userFactory; this.accountGetGroups = accountGetGroups; this.json = json; - this.options = EnumSet.noneOf(ListGroupsOption.class); + } + + public void setOptions(EnumSet<ListGroupsOption> options) { + this.options = options; } public Account.Id getUser() { @@ -132,16 +160,16 @@ } @Override - public Object apply(TopLevelResource resource) throws OrmException { - final Map<String, GroupInfo> output = Maps.newTreeMap(); + public SortedMap<String, GroupInfo> apply(TopLevelResource resource) + throws OrmException { + SortedMap<String, GroupInfo> output = Maps.newTreeMap(); for (GroupInfo info : get()) { output.put(MoreObjects.firstNonNull( info.name, "Group " + Url.decode(info.id)), info); info.name = null; } - return OutputFormat.JSON.newGson().toJsonTree(output, - new TypeToken<Map<String, GroupInfo>>() {}.getType()); + return output; } public List<GroupInfo> get() throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListIncludedGroups.java index 671486c..8e22ef9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListIncludedGroups.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListIncludedGroups.java
@@ -18,12 +18,12 @@ import com.google.common.collect.Lists; import com.google.gerrit.common.errors.NoSuchGroupException; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.reviewdb.client.AccountGroupById; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.account.GroupControl; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java index 9768270..abaa317 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutGroup.java
@@ -14,16 +14,16 @@ package com.google.gerrit.server.group; +import com.google.gerrit.extensions.api.groups.GroupInput; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; -import com.google.gerrit.server.group.CreateGroup.Input; import com.google.inject.Singleton; @Singleton -public class PutGroup implements RestModifyView<GroupResource, Input> { +public class PutGroup implements RestModifyView<GroupResource, GroupInput> { @Override - public Response<?> apply(GroupResource resource, Input input) + public Response<?> apply(GroupResource resource, GroupInput input) throws ResourceConflictException { throw new ResourceConflictException("Group already exists"); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java index 6d980ae..ba28cd1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutName.java
@@ -15,8 +15,7 @@ package com.google.gerrit.server.group; import com.google.common.base.Strings; -import com.google.gerrit.common.errors.InvalidNameException; -import com.google.gerrit.common.errors.NameAlreadyUsedException; +import com.google.gerrit.common.data.GroupDetail; import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; @@ -25,12 +24,24 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestModifyView; -import com.google.gerrit.server.account.PerformRenameGroup; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.reviewdb.client.AccountGroupName; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.account.GroupDetailFactory; +import com.google.gerrit.server.git.RenameGroupOp; import com.google.gerrit.server.group.PutName.Input; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; +import java.util.Collections; +import java.util.Date; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + @Singleton public class PutName implements RestModifyView<GroupResource, Input> { public static class Input { @@ -38,39 +49,92 @@ public String name; } - private final PerformRenameGroup.Factory performRenameGroupFactory; + private final Provider<ReviewDb> db; + private final GroupCache groupCache; + private final GroupDetailFactory.Factory groupDetailFactory; + private final RenameGroupOp.Factory renameGroupOpFactory; + private final Provider<IdentifiedUser> currentUser; @Inject - PutName(PerformRenameGroup.Factory performRenameGroupFactory) { - this.performRenameGroupFactory = performRenameGroupFactory; + PutName(Provider<ReviewDb> db, + GroupCache groupCache, + GroupDetailFactory.Factory groupDetailFactory, + RenameGroupOp.Factory renameGroupOpFactory, + Provider<IdentifiedUser> currentUser) { + this.db = db; + this.groupCache = groupCache; + this.groupDetailFactory = groupDetailFactory; + this.renameGroupOpFactory = renameGroupOpFactory; + this.currentUser = currentUser; } @Override - public String apply(GroupResource resource, Input input) + public String apply(GroupResource rsrc, Input input) throws MethodNotAllowedException, AuthException, BadRequestException, - ResourceNotFoundException, ResourceConflictException, OrmException { - if (resource.toAccountGroup() == null) { + ResourceNotFoundException, ResourceConflictException, OrmException, + NoSuchGroupException { + if (rsrc.toAccountGroup() == null) { throw new MethodNotAllowedException(); - } else if (!resource.getControl().isOwner()) { + } else if (!rsrc.getControl().isOwner()) { throw new AuthException("Not group owner"); } else if (input == null || Strings.isNullOrEmpty(input.name)) { throw new BadRequestException("name is required"); } + String newName = input.name.trim(); + if (newName.isEmpty()) { + throw new BadRequestException("name is required"); + } - final String newName = input.name.trim(); - if (resource.toAccountGroup().getName().equals(newName)) { + if (rsrc.toAccountGroup().getName().equals(newName)) { return newName; } + return renameGroup(rsrc.toAccountGroup(), newName).group.getName(); + } + + private GroupDetail renameGroup(AccountGroup group, String newName) + throws ResourceConflictException, OrmException, + NoSuchGroupException { + AccountGroup.Id groupId = group.getId(); + AccountGroup.NameKey old = group.getNameKey(); + AccountGroup.NameKey key = new AccountGroup.NameKey(newName); + try { - return performRenameGroupFactory.create().renameGroup( - resource.toAccountGroup().getId(), newName).group.getName(); - } catch (NoSuchGroupException e) { - throw new ResourceNotFoundException(); - } catch (InvalidNameException e) { - throw new BadRequestException(e.getMessage()); - } catch (NameAlreadyUsedException e) { - throw new ResourceConflictException(e.getMessage()); + AccountGroupName id = new AccountGroupName(key, groupId); + db.get().accountGroupNames().insert(Collections.singleton(id)); + } catch (OrmException e) { + AccountGroupName other = db.get().accountGroupNames().get(key); + if (other != null) { + // If we are using this identity, don't report the exception. + // + if (other.getId().equals(groupId)) { + return groupDetailFactory.create(groupId).call(); + } + + // Otherwise, someone else has this identity. + // + throw new ResourceConflictException("group with name " + newName + + "already exists"); + } else { + throw e; + } } + + group.setNameKey(key); + db.get().accountGroups().update(Collections.singleton(group)); + + AccountGroupName priorName = db.get().accountGroupNames().get(old); + if (priorName != null) { + db.get().accountGroupNames().delete(Collections.singleton(priorName)); + } + + groupCache.evict(group); + groupCache.evictAfterRename(old, key); + renameGroupOpFactory.create( + currentUser.get().newCommitterIdent(new Date(), TimeZone.getDefault()), + group.getGroupUUID(), + old.get(), newName).start(0, TimeUnit.MILLISECONDS); + + return groupDetailFactory.create(groupId).call(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java index 6ed6703..5788503 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java
@@ -14,6 +14,7 @@ package com.google.gerrit.server.group; +import com.google.gerrit.extensions.common.GroupOptionsInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.MethodNotAllowedException; @@ -22,7 +23,6 @@ import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.account.GroupCache; -import com.google.gerrit.server.group.PutOptions.Input; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -31,11 +31,8 @@ import java.util.Collections; @Singleton -public class PutOptions implements RestModifyView<GroupResource, Input> { - public static class Input { - public Boolean visibleToAll; - } - +public class PutOptions + implements RestModifyView<GroupResource, GroupOptionsInfo> { private final GroupCache groupCache; private final Provider<ReviewDb> db; @@ -46,7 +43,7 @@ } @Override - public GroupOptionsInfo apply(GroupResource resource, Input input) + public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input) throws MethodNotAllowedException, AuthException, BadRequestException, ResourceNotFoundException, OrmException { if (resource.toAccountGroup() == null) { @@ -72,6 +69,10 @@ db.get().accountGroups().update(Collections.singleton(group)); groupCache.evict(group); - return new GroupOptionsInfo(group); + GroupOptionsInfo options = new GroupOptionsInfo(); + if (group.isVisibleToAll()) { + options.visibleToAll = true; + } + return options; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java index 11d34ab..b88ead5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOwner.java
@@ -16,6 +16,7 @@ import com.google.common.base.Strings; import com.google.gerrit.common.data.GroupDescription; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.DefaultInput; @@ -26,7 +27,6 @@ import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.account.GroupCache; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gerrit.server.group.PutOwner.Input; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java index 2993739..9054ba4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -44,6 +44,7 @@ import java.io.IOException; import java.sql.Timestamp; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -54,6 +55,10 @@ * {@link ChangeQueryBuilder} for querying that field, and a method on * {@link ChangeData} used for populating the corresponding document fields in * the secondary index. + * <p> + * Field names are all lowercase alphanumeric plus underscore; index + * implementations may create unambiguous derived field names containing other + * characters. */ public class ChangeField { /** Legacy change ID. */ @@ -141,18 +146,26 @@ } }; + @Deprecated /** Topic, a short annotation on the branch. */ - public static final FieldDef<ChangeData, String> TOPIC = + public static final FieldDef<ChangeData, String> LEGACY_TOPIC = new FieldDef.Single<ChangeData, String>( "topic2", FieldType.EXACT, false) { @Override public String get(ChangeData input, FillArgs args) throws OrmException { - Change c = input.change(); - if (c == null) { - return null; - } - return firstNonNull(c.getTopic(), ""); + return getTopic(input); + } + }; + + /** Topic, a short annotation on the branch. */ + public static final FieldDef<ChangeData, String> TOPIC = + new FieldDef.Single<ChangeData, String>( + "topic3", FieldType.PREFIX, false) { + @Override + public String get(ChangeData input, FillArgs args) + throws OrmException { + return getTopic(input); } }; @@ -253,7 +266,7 @@ throws OrmException { Change c = input.change(); if (c == null) { - return null; + return ImmutableSet.of(); } Set<Integer> r = Sets.newHashSet(); if (!args.allowsDrafts && c.getStatus() == Change.Status.DRAFT) { @@ -274,7 +287,7 @@ public Iterable<String> get(ChangeData input, FillArgs args) throws OrmException { Set<String> revisions = Sets.newHashSet(); - for (PatchSet ps : input.patches()) { + for (PatchSet ps : input.patchSets()) { if (ps.getRevision() != null) { revisions.add(ps.getRevision().get()); } @@ -293,7 +306,7 @@ try { List<FooterLine> footers = input.commitFooters(); if (footers == null) { - return null; + return ImmutableSet.of(); } return Sets.newHashSet( args.trackingFooters.extract(footers).values()); @@ -498,6 +511,53 @@ } }; + /** Users who have commented on this change. */ + public static final FieldDef<ChangeData, Iterable<Integer>> COMMENTBY = + new FieldDef.Repeatable<ChangeData, Integer>( + ChangeQueryBuilder.FIELD_COMMENTBY, FieldType.INTEGER, false) { + @Override + public Iterable<Integer> get(ChangeData input, FillArgs args) + throws OrmException { + Set<Integer> r = new HashSet<>(); + for (ChangeMessage m : input.messages()) { + if (m.getAuthor() != null) { + r.add(m.getAuthor().get()); + } + } + for (PatchLineComment c : input.publishedComments()) { + r.add(c.getAuthor().get()); + } + return r; + } + }; + + public static class PatchSetProtoField + extends FieldDef.Repeatable<ChangeData, byte[]> { + public static final ProtobufCodec<PatchSet> CODEC = + CodecFactory.encoder(PatchSet.class); + + private PatchSetProtoField() { + super("_patch_set", FieldType.STORED_ONLY, true); + } + + @Override + public Iterable<byte[]> get(ChangeData input, FieldDef.FillArgs args) + throws OrmException { + return toProtos(CODEC, input.patchSets()); + } + } + + /** Serialized patch set object, used for pre-populating results. */ + public static final PatchSetProtoField PATCH_SET = new PatchSetProtoField(); + + private static String getTopic(ChangeData input) throws OrmException { + Change c = input.change(); + if (c == null) { + return null; + } + return firstNonNull(c.getTopic(), ""); + } + private static <T> List<byte[]> toProtos(ProtobufCodec<T> codec, Collection<T> objs) throws OrmException { List<byte[]> result = Lists.newArrayListWithCapacity(objs.size());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java index 05bf9bd..cff654a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -38,7 +38,7 @@ ChangeField.PROJECT, ChangeField.PROJECTS, ChangeField.REF, - ChangeField.TOPIC, + ChangeField.LEGACY_TOPIC, ChangeField.UPDATED, ChangeField.FILE_PART, ChangeField.PATH, @@ -68,7 +68,7 @@ ChangeField.PROJECT, ChangeField.PROJECTS, ChangeField.REF, - ChangeField.TOPIC, + ChangeField.LEGACY_TOPIC, ChangeField.UPDATED, ChangeField.FILE_PART, ChangeField.PATH, @@ -88,6 +88,7 @@ ChangeField.DELTA, ChangeField.HASHTAG); + @SuppressWarnings("deprecation") static final Schema<ChangeData> V14 = schema( ChangeField.LEGACY_ID, ChangeField.ID, @@ -95,6 +96,62 @@ ChangeField.PROJECT, ChangeField.PROJECTS, ChangeField.REF, + ChangeField.LEGACY_TOPIC, + ChangeField.UPDATED, + ChangeField.FILE_PART, + ChangeField.PATH, + ChangeField.OWNER, + ChangeField.REVIEWER, + ChangeField.COMMIT, + ChangeField.TR, + ChangeField.LABEL, + ChangeField.REVIEWED, + ChangeField.COMMIT_MESSAGE, + ChangeField.COMMENT, + ChangeField.CHANGE, + ChangeField.APPROVAL, + ChangeField.MERGEABLE, + ChangeField.ADDED, + ChangeField.DELETED, + ChangeField.DELTA, + ChangeField.HASHTAG); + + @SuppressWarnings("deprecation") + static final Schema<ChangeData> V15 = schema( + ChangeField.LEGACY_ID, + ChangeField.ID, + ChangeField.STATUS, + ChangeField.PROJECT, + ChangeField.PROJECTS, + ChangeField.REF, + ChangeField.LEGACY_TOPIC, + ChangeField.UPDATED, + ChangeField.FILE_PART, + ChangeField.PATH, + ChangeField.OWNER, + ChangeField.REVIEWER, + ChangeField.COMMIT, + ChangeField.TR, + ChangeField.LABEL, + ChangeField.REVIEWED, + ChangeField.COMMIT_MESSAGE, + ChangeField.COMMENT, + ChangeField.CHANGE, + ChangeField.APPROVAL, + ChangeField.MERGEABLE, + ChangeField.ADDED, + ChangeField.DELETED, + ChangeField.DELTA, + ChangeField.HASHTAG, + ChangeField.COMMENTBY); + + static final Schema<ChangeData> V16 = schema( + ChangeField.LEGACY_ID, + ChangeField.ID, + ChangeField.STATUS, + ChangeField.PROJECT, + ChangeField.PROJECTS, + ChangeField.REF, ChangeField.TOPIC, ChangeField.UPDATED, ChangeField.FILE_PART, @@ -113,7 +170,37 @@ ChangeField.ADDED, ChangeField.DELETED, ChangeField.DELTA, - ChangeField.HASHTAG); + ChangeField.HASHTAG, + ChangeField.COMMENTBY); + + static final Schema<ChangeData> V17 = schema( + ChangeField.LEGACY_ID, + ChangeField.ID, + ChangeField.STATUS, + ChangeField.PROJECT, + ChangeField.PROJECTS, + ChangeField.REF, + ChangeField.TOPIC, + ChangeField.UPDATED, + ChangeField.FILE_PART, + ChangeField.PATH, + ChangeField.OWNER, + ChangeField.REVIEWER, + ChangeField.COMMIT, + ChangeField.TR, + ChangeField.LABEL, + ChangeField.REVIEWED, + ChangeField.COMMIT_MESSAGE, + ChangeField.COMMENT, + ChangeField.CHANGE, + ChangeField.APPROVAL, + ChangeField.MERGEABLE, + ChangeField.ADDED, + ChangeField.DELETED, + ChangeField.DELTA, + ChangeField.HASHTAG, + ChangeField.COMMENTBY, + ChangeField.PATCH_SET); private static Schema<ChangeData> schema(Collection<FieldDef<ChangeData, ?>> fields) { return new Schema<>(ImmutableList.copyOf(fields));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java index 557faeb..cf3fd09 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
@@ -14,6 +14,9 @@ package com.google.gerrit.server.index; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.CharMatcher; import com.google.common.base.Preconditions; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.TrackingFooters; @@ -78,11 +81,17 @@ private final boolean stored; private FieldDef(String name, FieldType<?> type, boolean stored) { - this.name = name; + this.name = checkName(name); this.type = type; this.stored = stored; } + private static String checkName(String name) { + CharMatcher m = CharMatcher.anyOf("abcdefghijklmnopqrstuvwxyz0123456789_"); + checkArgument(m.matchesAllOf(name), "illegal field name: %s", name); + return name; + } + /** @return name of the field. */ public final String getName() { return name;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java index dce8a20..89dc808 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
@@ -65,4 +65,8 @@ public String toString() { return name; } + + public static IllegalArgumentException badFieldType(FieldType<?> t) { + return new IllegalArgumentException("unknown index field type " + t); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexConfig.java index 1857e55..08f4748 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexConfig.java
@@ -18,6 +18,8 @@ import com.google.auto.value.AutoValue; +import org.eclipse.jgit.lib.Config; + /** * Implementation-specific configuration for secondary indexes. * <p> @@ -28,13 +30,30 @@ @AutoValue public abstract class IndexConfig { public static IndexConfig createDefault() { - return create(Integer.MAX_VALUE); + return create(0, 0); } - public static IndexConfig create(int maxLimit) { - checkArgument(maxLimit > 0, "maxLimit must be positive: %s", maxLimit); - return new AutoValue_IndexConfig(maxLimit); + public static IndexConfig fromConfig(Config cfg) { + return create( + cfg.getInt("index", null, "maxLimit", 0), + cfg.getInt("index", null, "maxPages", 0)); + } + + public static IndexConfig create(int maxLimit, int maxPages) { + return new AutoValue_IndexConfig( + checkLimit(maxLimit, "maxLimit"), + checkLimit(maxPages, "maxPages")); + } + + private static int checkLimit(int limit, String name) { + if (limit == 0) { + return Integer.MAX_VALUE; + } + checkArgument(limit > 0, "%s must be positive: %s", name, limit); + return limit; } public abstract int maxLimit(); + + public abstract int maxPages(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java index adcf242..409c155 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
@@ -17,6 +17,7 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -25,12 +26,13 @@ public static interface Factory extends ReplyToChangeSender.Factory<AbandonedSender> { @Override - AbandonedSender create(Change change); + AbandonedSender create(Change.Id change); } @Inject - public AbandonedSender(EmailArguments ea, @Assisted Change c) { - super(ea, c, "abandon"); + public AbandonedSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "abandon", newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java index c181a9c..7a6d204 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java
@@ -16,18 +16,20 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; /** Asks a user to review a change. */ public class AddReviewerSender extends NewChangeSender { public static interface Factory { - AddReviewerSender create(Change change); + AddReviewerSender create(Change.Id id); } @Inject - public AddReviewerSender(EmailArguments ea, @Assisted Change c) { - super(ea, c); + public AddReviewerSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java index ac23455..689c596 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -55,6 +55,10 @@ public abstract class ChangeEmail extends NotificationEmail { private static final Logger log = LoggerFactory.getLogger(ChangeEmail.class); + protected static ChangeData newChangeData(EmailArguments ea, Change.Id id) { + return ea.changeDataFactory.create(ea.db.get(), id); + } + protected final Change change; protected final ChangeData changeData; protected PatchSet patchSet; @@ -65,10 +69,11 @@ protected Set<Account.Id> authors; protected boolean emailOnlyAuthors; - protected ChangeEmail(EmailArguments ea, Change c, String mc) { - super(ea, mc, c.getProject(), c.getDest()); - change = c; - changeData = ea.changeDataFactory.create(ea.db.get(), c); + protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) + throws OrmException { + super(ea, mc, cd.change().getDest()); + changeData = cd; + change = cd.change(); emailOnlyAuthors = false; } @@ -305,7 +310,8 @@ @Override protected final Watchers getWatchers(NotifyType type) throws OrmException { - ProjectWatch watch = new ProjectWatch(args, project, projectState, changeData); + ProjectWatch watch = new ProjectWatch( + args, branch.getParentKey(), projectState, changeData); return watch.getWatchers(type); } @@ -379,6 +385,8 @@ return args.settings.includeDiff; } + private static int HEAP_EST_SIZE = 32 * 1024; + /** Show patch set as unified difference. */ public String getUnifiedDiff() { PatchList patchList; @@ -394,8 +402,9 @@ return ""; } + int maxSize = args.settings.maximumDiffSize; TemporaryBuffer.Heap buf = - new TemporaryBuffer.Heap(args.settings.maximumDiffSize); + new TemporaryBuffer.Heap(Math.min(HEAP_EST_SIZE, maxSize), maxSize); try (DiffFormatter fmt = new DiffFormatter(buf)) { Repository git; try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java index b587791..8147cff 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -14,6 +14,8 @@ package com.google.gerrit.server.mail; +import static com.google.gerrit.server.PatchLineCommentsUtil.getCommentPsId; + import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.Ordering; @@ -50,7 +52,7 @@ .getLogger(CommentSender.class); public static interface Factory { - public CommentSender create(NotifyHandling notify, Change change); + public CommentSender create(NotifyHandling notify, Change.Id id); } private final NotifyHandling notify; @@ -59,10 +61,10 @@ @Inject public CommentSender(EmailArguments ea, + PatchLineCommentsUtil plcUtil, @Assisted NotifyHandling notify, - @Assisted Change c, - PatchLineCommentsUtil plcUtil) { - super(ea, c, "comment"); + @Assisted Change.Id id) throws OrmException { + super(ea, "comment", newChangeData(ea, id)); this.notify = notify; this.plcUtil = plcUtil; } @@ -175,7 +177,8 @@ short side = comment.getSide(); CommentRange range = comment.getRange(); if (range != null) { - String prefix = String.format("Line %d: ", range.getStartLine()); + String prefix = "PS" + getCommentPsId(comment).get() + + ", Line " + range.getStartLine() + ": "; for (int n = range.getStartLine(); n <= range.getEndLine(); n++) { out.append(n == range.getStartLine() ? prefix
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java index f570ac8..29895d9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
@@ -33,12 +33,13 @@ LoggerFactory.getLogger(CreateChangeSender.class); public static interface Factory { - public CreateChangeSender create(Change change); + public CreateChangeSender create(Change.Id id); } @Inject - public CreateChangeSender(EmailArguments ea, @Assisted Change c) { - super(ea, c); + public CreateChangeSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java index 46e151b..592fbb0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java
@@ -14,6 +14,8 @@ package com.google.gerrit.server.mail; +import com.google.common.base.MoreObjects; + import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.Writer; @@ -23,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Objects; public abstract class EmailHeader { public abstract boolean isEmpty(); @@ -30,7 +33,7 @@ public abstract void write(Writer w) throws IOException; public static class String extends EmailHeader { - private java.lang.String value; + private final java.lang.String value; public String(java.lang.String v) { value = v; @@ -53,6 +56,22 @@ w.write(value); } } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object o) { + return (o instanceof String) + && Objects.equals(value, ((String) o).value); + } + + @Override + public java.lang.String toString() { + return MoreObjects.toStringHelper(this).addValue(value).toString(); + } } static boolean needsQuotedPrintable(java.lang.String value) { @@ -113,7 +132,7 @@ } public static class Date extends EmailHeader { - private java.util.Date value; + private final java.util.Date value; public Date(java.util.Date v) { value = v; @@ -135,6 +154,22 @@ fmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH); w.write(fmt.format(value)); } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object o) { + return (o instanceof Date) + && Objects.equals(value, ((Date) o).value); + } + + @Override + public java.lang.String toString() { + return MoreObjects.toStringHelper(this).addValue(value).toString(); + } } public static class AddressList extends EmailHeader { @@ -191,5 +226,21 @@ needComma = true; } } + + @Override + public int hashCode() { + return Objects.hashCode(list); + } + + @Override + public boolean equals(Object o) { + return (o instanceof AddressList) + && Objects.equals(list, ((AddressList) o).list); + } + + @Override + public java.lang.String toString() { + return MoreObjects.toStringHelper(this).addValue(list).toString(); + } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java index 058bbc8..51f7ad1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java
@@ -41,7 +41,7 @@ @Inject FromAddressGeneratorProvider(@GerritServerConfig final Config cfg, - final @AnonymousCowardName String anonymousCowardName, + @AnonymousCowardName final String anonymousCowardName, @GerritPersonIdent final PersonIdent myIdent, final AccountCache accountCache) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java index 3a5f7eb..ba75723 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java
@@ -16,18 +16,20 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; /** Send notice about a change failing to merged. */ public class MergeFailSender extends ReplyToChangeSender { public static interface Factory { - public MergeFailSender create(Change change); + public MergeFailSender create(Change.Id id); } @Inject - public MergeFailSender(EmailArguments ea, @Assisted Change c) { - super(ea, c, "merge-failed"); + public MergeFailSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "merge-failed", newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java index 5cb1ba1..7fbcf8d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
@@ -22,8 +22,8 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; +import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSetApproval; -import com.google.gerrit.server.project.ChangeControl; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -31,15 +31,16 @@ /** Send notice about a change successfully merged. */ public class MergedSender extends ReplyToChangeSender { public static interface Factory { - public MergedSender create(ChangeControl change); + public MergedSender create(Change.Id id); } private final LabelTypes labelTypes; @Inject - public MergedSender(EmailArguments ea, @Assisted ChangeControl c) { - super(ea, c.getChange(), "merged"); - labelTypes = c.getLabelTypes(); + public MergedSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "merged", newChangeData(ea, id)); + labelTypes = changeData.changeControl().getLabelTypes(); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java index 0dbcbe0..e18c7e5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
@@ -16,7 +16,8 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; import java.util.ArrayList; import java.util.Collection; @@ -29,8 +30,9 @@ private final Set<Account.Id> reviewers = new HashSet<>(); private final Set<Account.Id> extraCC = new HashSet<>(); - protected NewChangeSender(EmailArguments ea, Change c) { - super(ea, c, "newchange"); + protected NewChangeSender(EmailArguments ea, ChangeData cd) + throws OrmException { + super(ea, "newchange", cd); } public void addReviewers(final Collection<Account.Id> cc) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NotificationEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NotificationEmail.java index f0c43d3..de338ec 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NotificationEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NotificationEmail.java
@@ -19,7 +19,6 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; import com.google.gerrit.reviewdb.client.Branch; -import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.mail.ProjectWatch.Watchers; import com.google.gwtorm.server.OrmException; @@ -33,14 +32,11 @@ private static final Logger log = LoggerFactory.getLogger(NotificationEmail.class); - protected Project.NameKey project; protected Branch.NameKey branch; protected NotificationEmail(EmailArguments ea, - String mc, Project.NameKey project, Branch.NameKey branch) { + String mc, Branch.NameKey branch) { super(ea, mc); - - this.project = project; this.branch = branch; } @@ -104,7 +100,7 @@ @Override protected void setupVelocityContext() { super.setupVelocityContext(); - velocityContext.put("projectName", project.get()); + velocityContext.put("projectName", branch.getParentKey().get()); velocityContext.put("branch", branch); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java index 53efa1e..80a5d24 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java
@@ -100,7 +100,8 @@ updatedPatchSet, info, recipients.getReviewers(), Collections.<Account.Id> emptySet()); try { - CreateChangeSender cm = createChangeSenderFactory.create(updatedChange); + CreateChangeSender cm = + createChangeSenderFactory.create(updatedChange.getId()); cm.setFrom(me); cm.setPatchSet(updatedPatchSet, info); cm.addReviewers(recipients.getReviewers()); @@ -119,7 +120,8 @@ updatedPatchSet.getCreatedOn(), updatedPatchSet.getId()); msg.setMessage("Uploaded patch set " + updatedPatchSet.getPatchSetId() + "."); try { - ReplacePatchSetSender cm = replacePatchSetFactory.create(updatedChange); + ReplacePatchSetSender cm = + replacePatchSetFactory.create(updatedChange.getId()); cm.setFrom(me); cm.setPatchSet(updatedPatchSet, info); cm.setChangeMessage(msg);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java index 63709c6..95b0219 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
@@ -87,10 +87,9 @@ try { add(matching, nc); } catch (QueryParseException e) { - log.warn(String.format( - "Project %s has invalid notify %s filter \"%s\"", + log.warn("Project {} has invalid notify {} filter \"{}\": {}", state.getProject().getName(), nc.getName(), - nc.getFilter()), e); + nc.getFilter(), e.getMessage()); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java index 8412d22..05c7933 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
@@ -18,6 +18,7 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -30,15 +31,16 @@ /** Send notice of new patch sets for reviewers. */ public class ReplacePatchSetSender extends ReplyToChangeSender { public static interface Factory { - public ReplacePatchSetSender create(Change change); + public ReplacePatchSetSender create(Change.Id id); } private final Set<Account.Id> reviewers = new HashSet<>(); private final Set<Account.Id> extraCC = new HashSet<>(); @Inject - public ReplacePatchSetSender(EmailArguments ea, @Assisted Change c) { - super(ea, c, "newpatchset"); + public ReplacePatchSetSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "newpatchset", newChangeData(ea, id)); } public void addReviewers(final Collection<Account.Id> cc) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java index 42ac917..62a6c72 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
@@ -16,15 +16,18 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gwtorm.server.OrmException; /** Alert a user to a reply to a change, usually commentary made during review. */ public abstract class ReplyToChangeSender extends ChangeEmail { public static interface Factory<T extends ReplyToChangeSender> { - public T create(Change change); + public T create(Change.Id id); } - protected ReplyToChangeSender(EmailArguments ea, Change c, String mc) { - super(ea, c, mc); + protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) + throws OrmException { + super(ea, mc, cd); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java index 4f65ab4..a43c7b6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
@@ -17,6 +17,7 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -25,12 +26,13 @@ public static interface Factory extends ReplyToChangeSender.Factory<RestoredSender> { @Override - RestoredSender create(Change change); + RestoredSender create(Change.Id id); } @Inject - public RestoredSender(EmailArguments ea, @Assisted Change c) { - super(ea, c, "restore"); + public RestoredSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "restore", newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java index d1389fb..dda68ab 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
@@ -17,18 +17,20 @@ import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType; import com.google.gerrit.reviewdb.client.Change; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; /** Send notice about a change being reverted. */ public class RevertedSender extends ReplyToChangeSender { public static interface Factory { - RevertedSender create(Change change); + RevertedSender create(Change.Id id); } @Inject - public RevertedSender(EmailArguments ea, @Assisted Change c) { - super(ea, c, "revert"); + public RevertedSender(EmailArguments ea, @Assisted Change.Id id) + throws OrmException { + super(ea, "revert", newChangeData(ea, id)); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java index ace1f5b..101aaac 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.file.Files; import java.util.Properties; /** Configures Velocity template engine for sending email. */ @@ -49,10 +50,11 @@ p.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, "true"); p.setProperty("runtime.log.logsystem.log4j.category", "velocity"); - if (site.mail_dir.isDirectory()) { + if (Files.isDirectory(site.mail_dir)) { p.setProperty(rl, "file, class"); p.setProperty("file." + rl + ".class", pkg + ".FileResourceLoader"); - p.setProperty("file." + rl + ".path", site.mail_dir.getAbsolutePath()); + p.setProperty("file." + rl + ".path", + site.mail_dir.toAbsolutePath().toString()); p.setProperty("class." + rl + ".class", pkg + ".ClasspathResourceLoader"); } else { p.setProperty(rl, "class");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java index 3b51bb4..47c1731 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -16,14 +16,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; +import static com.google.gerrit.server.notedb.CommentsInNotesUtil.addCommentToMap; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Table; +import com.google.common.collect.Sets; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment.Status; -import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RevId; @@ -46,8 +46,12 @@ import org.eclipse.jgit.revwalk.RevCommit; import java.io.IOException; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -165,11 +169,6 @@ } private void verifyComment(PatchLineComment comment) { - checkState(psId != null, - "setPatchSetId must be called first"); - checkArgument(getCommentPsId(comment).equals(psId), - "Comment on %s does not match configured patch set %s", - getCommentPsId(comment), psId); if (migration.writeChanges()) { checkArgument(comment.getRevId() != null); } @@ -190,76 +189,55 @@ noteMap = NoteMap.newEmptyMap(); } - Table<PatchSet.Id, String, PatchLineComment> baseDrafts = - draftNotes.getDraftBaseComments(); - Table<PatchSet.Id, String, PatchLineComment> psDrafts = - draftNotes.getDraftPsComments(); + Map<RevId, List<PatchLineComment>> allComments = new HashMap<>(); - boolean draftsEmpty = baseDrafts.isEmpty() && psDrafts.isEmpty(); - - // There is no need to rewrite the note for one of the sides of the patch - // set if all of the modifications were made to the comments of one side, - // so we set these flags to potentially save that extra work. - boolean baseSideChanged = false; - boolean revisionSideChanged = false; - - // We must define these RevIds so that if this update deletes all - // remaining comments on a given side, then we can remove that note. - // However, if this update doesn't delete any comments, it is okay for these - // to be null because they won't be used. - RevId baseRevId = null; - RevId psRevId = null; - + boolean hasComments = false; + int n = deleteComments.size() + upsertComments.size(); + Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(n); + Set<PatchLineComment.Key> updatedKeys = Sets.newHashSetWithExpectedSize(n); for (PatchLineComment c : deleteComments) { - if (c.getSide() == (short) 0) { - baseSideChanged = true; - baseRevId = c.getRevId(); - baseDrafts.remove(psId, c.getKey().get()); - } else { - revisionSideChanged = true; - psRevId = c.getRevId(); - psDrafts.remove(psId, c.getKey().get()); - } + allComments.put(c.getRevId(), new ArrayList<PatchLineComment>()); + updatedRevs.add(c.getRevId()); + updatedKeys.add(c.getKey()); } for (PatchLineComment c : upsertComments) { - if (c.getSide() == (short) 0) { - baseSideChanged = true; - baseDrafts.put(psId, c.getKey().get(), c); - } else { - revisionSideChanged = true; - psDrafts.put(psId, c.getKey().get(), c); + hasComments = true; + addCommentToMap(allComments, c); + updatedRevs.add(c.getRevId()); + updatedKeys.add(c.getKey()); + } + + // Re-add old comments for updated revisions so the new note contents + // includes both old and new comments merged in the right order. + // + // writeCommentsToNoteMap doesn't touch notes for SHA-1s that are not + // mentioned in the input map, so by omitting comments for those revisions, + // we avoid the work of having to re-serialize identical comment data for + // those revisions. + ListMultimap<RevId, PatchLineComment> existing = + draftNotes.getComments(); + for (Map.Entry<RevId, PatchLineComment> e : existing.entries()) { + PatchLineComment c = e.getValue(); + if (updatedRevs.contains(c.getRevId()) + && !updatedKeys.contains(c.getKey())) { + hasComments = true; + addCommentToMap(allComments, e.getValue()); } } - List<PatchLineComment> newBaseDrafts = - Lists.newArrayList(baseDrafts.row(psId).values()); - List<PatchLineComment> newPsDrafts = - Lists.newArrayList(psDrafts.row(psId).values()); + // If we touched every revision and there are no comments left, set the flag + // for the caller to delete the entire ref. + boolean touchedAllRevs = updatedRevs.equals(existing.keySet()); + if (touchedAllRevs && !hasComments) { + removedAllComments.set(touchedAllRevs && !hasComments); + return null; + } - updateNoteMap(baseSideChanged, noteMap, newBaseDrafts, - baseRevId); - updateNoteMap(revisionSideChanged, noteMap, newPsDrafts, - psRevId); - - removedAllComments.set( - baseDrafts.isEmpty() && psDrafts.isEmpty() && !draftsEmpty); - + commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter); return noteMap.writeTree(inserter); } - private void updateNoteMap(boolean changed, NoteMap noteMap, - List<PatchLineComment> comments, RevId commitId) - throws IOException { - if (changed) { - if (comments.isEmpty()) { - commentsUtil.removeNote(noteMap, commitId); - } else { - commentsUtil.writeCommentsToNoteMap(noteMap, comments, inserter); - } - } - } - public RevCommit commit() throws IOException { BatchMetaDataUpdate batch = openUpdate(); try { @@ -279,13 +257,11 @@ if (migration.writeChanges()) { AtomicBoolean removedAllComments = new AtomicBoolean(); ObjectId treeId = storeCommentsInNotes(removedAllComments); - if (treeId != null) { - if (removedAllComments.get()) { - batch.removeRef(getRefName()); - } else { - builder.setTreeId(treeId); - batch.write(builder); - } + if (removedAllComments.get()) { + batch.removeRef(getRefName()); + } else if (treeId != null) { + builder.setTreeId(treeId); + batch.write(builder); } } } @@ -308,7 +284,7 @@ } commit.setAuthor(newIdent(getUser().getAccount(), when)); commit.setCommitter(new PersonIdent(serverIdent, when)); - commit.setMessage(String.format("Comment on patch set %d", psId.get())); + commit.setMessage("Update draft comments"); return true; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java index 8cb72e9..063ff5a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -15,18 +15,15 @@ package com.google.gerrit.server.notedb; import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST; -import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Ordering; -import com.google.common.collect.Table; import com.google.common.primitives.Ints; import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.reviewdb.client.Account; @@ -36,6 +33,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.git.GitRepositoryManager; @@ -52,8 +50,6 @@ import java.io.IOException; import java.sql.Timestamp; -import java.util.Comparator; -import java.util.List; import java.util.Map; /** View of a single {@link Change} based on the log of its notes branch. */ @@ -76,20 +72,6 @@ } }); - public static Comparator<PatchLineComment> PLC_ORDER = - new Comparator<PatchLineComment>() { - @Override - public int compare(PatchLineComment c1, PatchLineComment c2) { - String filename1 = c1.getKey().getParentKey().get(); - String filename2 = c2.getKey().getParentKey().get(); - return ComparisonChain.start() - .compare(filename1, filename2) - .compare(c1.getLine(), c2.getLine()) - .compare(c1.getWrittenOn(), c2.getWrittenOn()) - .result(); - } - }; - public static ConfigInvalidException parseException(Change.Id changeId, String fmt, Object... args) { return new ConfigInvalidException("Change " + changeId + ": " @@ -138,8 +120,7 @@ private ImmutableList<Account.Id> allPastReviewers; private ImmutableList<SubmitRecord> submitRecords; private ImmutableListMultimap<PatchSet.Id, ChangeMessage> changeMessages; - private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForBase; - private ImmutableListMultimap<PatchSet.Id, PatchLineComment> commentsForPS; + private ImmutableListMultimap<RevId, PatchLineComment> comments; private ImmutableSet<String> hashtags; NoteMap noteMap; @@ -194,28 +175,15 @@ return changeMessages; } - /** @return inline comments on each patchset's base (side == 0). */ - public ImmutableListMultimap<PatchSet.Id, PatchLineComment> - getBaseComments() { - return commentsForBase; + /** @return inline comments on each revision. */ + public ImmutableListMultimap<RevId, PatchLineComment> getComments() { + return comments; } - /** @return inline comments on each patchset (side == 1). */ - public ImmutableListMultimap<PatchSet.Id, PatchLineComment> - getPatchSetComments() { - return commentsForPS; - } - - public Table<PatchSet.Id, String, PatchLineComment> getDraftBaseComments( + public ImmutableListMultimap<RevId, PatchLineComment> getDraftComments( Account.Id author) throws OrmException { loadDraftComments(author); - return draftCommentNotes.getDraftBaseComments(); - } - - public Table<PatchSet.Id, String, PatchLineComment> getDraftPsComments( - Account.Id author) throws OrmException { - loadDraftComments(author); - return draftCommentNotes.getDraftPsComments(); + return draftCommentNotes.getComments(); } /** @@ -234,6 +202,11 @@ } } + @VisibleForTesting + DraftCommentNotes getDraftCommentNotes() { + return draftCommentNotes; + } + public boolean containsComment(PatchLineComment c) throws OrmException { if (containsCommentPublished(c)) { return true; @@ -243,11 +216,7 @@ } public boolean containsCommentPublished(PatchLineComment c) { - PatchSet.Id psId = getCommentPsId(c); - List<PatchLineComment> list = (c.getSide() == (short) 0) - ? getBaseComments().get(psId) - : getPatchSetComments().get(psId); - for (PatchLineComment l : list) { + for (PatchLineComment l : getComments().values()) { if (c.getKey().equals(l.getKey())) { return true; } @@ -282,8 +251,7 @@ } approvals = parser.buildApprovals(); changeMessages = parser.buildMessages(); - commentsForBase = ImmutableListMultimap.copyOf(parser.commentsForBase); - commentsForPS = ImmutableListMultimap.copyOf(parser.commentsForPs); + comments = ImmutableListMultimap.copyOf(parser.comments); noteMap = parser.commentNoteMap; if (parser.hashtags != null) { @@ -310,8 +278,7 @@ reviewers = ImmutableSetMultimap.of(); submitRecords = ImmutableList.of(); changeMessages = ImmutableListMultimap.of(); - commentsForBase = ImmutableListMultimap.of(); - commentsForPS = ImmutableListMultimap.of(); + comments = ImmutableListMultimap.of(); hashtags = ImmutableSet.of(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java index b5b3c74..6f8cb2b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -44,6 +44,7 @@ import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.util.LabelVote; @@ -72,8 +73,7 @@ final Map<Account.Id, ReviewerState> reviewers; final List<Account.Id> allPastReviewers; final List<SubmitRecord> submitRecords; - final Multimap<PatchSet.Id, PatchLineComment> commentsForPs; - final Multimap<PatchSet.Id, PatchLineComment> commentsForBase; + final Multimap<RevId, PatchLineComment> comments; NoteMap commentNoteMap; Change.Status status; Set<String> hashtags; @@ -99,8 +99,7 @@ allPastReviewers = Lists.newArrayList(); submitRecords = Lists.newArrayListWithExpectedSize(1); changeMessages = LinkedListMultimap.create(); - commentsForPs = ArrayListMultimap.create(); - commentsForBase = ArrayListMultimap.create(); + comments = ArrayListMultimap.create(); } @Override @@ -275,7 +274,7 @@ throws IOException, ConfigInvalidException { commentNoteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo, ChangeNoteUtil.changeRefName(changeId), walk, changeId, - commentsForBase, commentsForPs, PatchLineComment.Status.PUBLISHED); + comments, PatchLineComment.Status.PUBLISHED); } private void parseApproval(PatchSet.Id psId, Account.Id accountId,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilder.java index 76dfdc8..d715947 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeRebuilder.java
@@ -16,7 +16,6 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId; -import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; import com.google.common.base.MoreObjects; import com.google.common.collect.ArrayListMultimap; @@ -33,6 +32,7 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.PatchLineCommentsUtil; import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate; import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.project.ChangeControl; @@ -306,7 +306,7 @@ PatchLineCommentEvent(PatchLineComment c, Change change, PatchSet ps, PatchListCache cache) { - super(getCommentPsId(c), c.getAuthor(), c.getWrittenOn()); + super(PatchLineCommentsUtil.getCommentPsId(c), c.getAuthor(), c.getWrittenOn()); this.c = c; this.change = change; this.ps = ps;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java index 7302425..43c232b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -20,7 +20,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_SUBMITTED_WITH; -import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; +import static com.google.gerrit.server.notedb.CommentsInNotesUtil.addCommentToMap; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; @@ -28,14 +28,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment.Status; -import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.config.AnonymousCowardName; @@ -57,6 +56,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.List; @@ -91,8 +91,7 @@ private String subject; private List<SubmitRecord> submitRecords; private final CommentsInNotesUtil commentsUtil; - private List<PatchLineComment> commentsForBase; - private List<PatchLineComment> commentsForPs; + private List<PatchLineComment> comments; private Set<String> hashtags; private String changeMessage; private ChangeNotes notes; @@ -161,8 +160,7 @@ this.commentsUtil = commentsUtil; this.approvals = Maps.newTreeMap(labelNameComparator); this.reviewers = Maps.newLinkedHashMap(); - this.commentsForPs = Lists.newArrayList(); - this.commentsForBase = Lists.newArrayList(); + this.comments = Lists.newArrayList(); } public void setStatus(Change.Status status) { @@ -238,15 +236,11 @@ "A comment already exists with the same key as the following comment," + " so we cannot insert this comment: %s", c); } - if (c.getSide() == 0) { - commentsForBase.add(c); - } else { - commentsForPs.add(c); - } + comments.add(c); } private void insertDraftComment(PatchLineComment c) throws OrmException { - createDraftUpdateIfNull(c); + createDraftUpdateIfNull(); draftUpdate.insertComment(c); } @@ -263,15 +257,11 @@ checkArgument(!notes.containsCommentPublished(c), "Cannot update a comment that has already been published and saved"); } - if (c.getSide() == 0) { - commentsForBase.add(c); - } else { - commentsForPs.add(c); - } + comments.add(c); } private void upsertDraftComment(PatchLineComment c) { - createDraftUpdateIfNull(c); + createDraftUpdateIfNull(); draftUpdate.upsertComment(c); } @@ -286,46 +276,32 @@ checkArgument(!notes.containsCommentPublished(c), "Cannot update a comment that has already been published and saved"); } - if (c.getSide() == 0) { - commentsForBase.add(c); - } else { - commentsForPs.add(c); - } + comments.add(c); } private void updateDraftComment(PatchLineComment c) throws OrmException { - createDraftUpdateIfNull(c); + createDraftUpdateIfNull(); draftUpdate.updateComment(c); } private void deleteDraftComment(PatchLineComment c) throws OrmException { - createDraftUpdateIfNull(c); + createDraftUpdateIfNull(); draftUpdate.deleteComment(c); } private void deleteDraftCommentIfPresent(PatchLineComment c) throws OrmException { - createDraftUpdateIfNull(c); + createDraftUpdateIfNull(); draftUpdate.deleteCommentIfPresent(c); } - private void createDraftUpdateIfNull(PatchLineComment c) { + private void createDraftUpdateIfNull() { if (draftUpdate == null) { draftUpdate = draftUpdateFactory.create(ctl, when); - if (psId != null) { - draftUpdate.setPatchSetId(psId); - } else { - draftUpdate.setPatchSetId(getCommentPsId(c)); - } } } private void verifyComment(PatchLineComment c) { - checkArgument(psId != null, - "setPatchSetId must be called first"); - checkArgument(getCommentPsId(c).equals(psId), - "Comment on %s doesn't match previous patch set %s", - getCommentPsId(c), psId); checkArgument(c.getRevId() != null); checkArgument(c.getStatus() == Status.PUBLISHED, "Cannot add a draft comment to a ChangeUpdate. Use a ChangeDraftUpdate" @@ -356,31 +332,23 @@ if (noteMap == null) { noteMap = NoteMap.newEmptyMap(); } - if (commentsForPs.isEmpty() && commentsForBase.isEmpty()) { + if (comments.isEmpty()) { return null; } - Multimap<PatchSet.Id, PatchLineComment> allCommentsOnBases = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> allCommentsOnPs = - notes.getPatchSetComments(); - - // This writes all comments for the base of this PS to the note map. - if (!commentsForBase.isEmpty()) { - List<PatchLineComment> baseCommentsForThisPs = - new ArrayList<>(allCommentsOnBases.get(psId)); - baseCommentsForThisPs.addAll(commentsForBase); - commentsUtil.writeCommentsToNoteMap(noteMap, baseCommentsForThisPs, - inserter); + Map<RevId, List<PatchLineComment>> allComments = Maps.newHashMap(); + for (Map.Entry<RevId, Collection<PatchLineComment>> e + : notes.getComments().asMap().entrySet()) { + List<PatchLineComment> comments = new ArrayList<>(); + for (PatchLineComment c : e.getValue()) { + comments.add(c); + } + allComments.put(e.getKey(), comments); } - - // This write all comments for this PS to the note map. - if (!commentsForPs.isEmpty()) { - List<PatchLineComment> commentsForThisPs = - new ArrayList<>(allCommentsOnPs.get(psId)); - commentsForThisPs.addAll(commentsForPs); - commentsUtil.writeCommentsToNoteMap(noteMap, commentsForThisPs, inserter); + for (PatchLineComment c : comments) { + addCommentToMap(allComments, c); } + commentsUtil.writeCommentsToNoteMap(noteMap, allComments, inserter); return noteMap.writeTree(inserter); } @@ -504,8 +472,7 @@ private boolean isEmpty() { return approvals.isEmpty() && changeMessage == null - && commentsForBase.isEmpty() - && commentsForPs.isEmpty() + && comments.isEmpty() && reviewers.isEmpty() && status == null && subject == null
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java index f3e03c0..149325d9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
@@ -15,6 +15,7 @@ package com.google.gerrit.server.notedb; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER; import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST; import static com.google.gerrit.server.notedb.ChangeNotes.parseException; import static java.nio.charset.StandardCharsets.UTF_8; @@ -33,6 +34,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.PatchLineCommentsUtil; import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.config.AnonymousCowardName; import com.google.inject.Inject; @@ -63,9 +65,11 @@ import java.nio.charset.Charset; import java.sql.Timestamp; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Map; /** * Utility functions to parse PatchLineComments out of a note byte array and @@ -86,8 +90,7 @@ public static NoteMap parseCommentsFromNotes(Repository repo, String refName, RevWalk walk, Change.Id changeId, - Multimap<PatchSet.Id, PatchLineComment> commentsForBase, - Multimap<PatchSet.Id, PatchLineComment> commentsForPs, + Multimap<RevId, PatchLineComment> comments, Status status) throws IOException, ConfigInvalidException { Ref ref = repo.getRef(refName); @@ -99,20 +102,14 @@ RevCommit commit = walk.parseCommit(ref.getObjectId()); NoteMap noteMap = NoteMap.read(reader, commit); - for (Note note: noteMap) { + for (Note note : noteMap) { byte[] bytes = reader.open(note.getData(), OBJ_BLOB).getCachedBytes(MAX_NOTE_SZ); List<PatchLineComment> result = parseNote(bytes, changeId, status); if (result == null || result.isEmpty()) { continue; } - PatchSet.Id psId = result.get(0).getKey().getParentKey().getParentKey(); - short side = result.get(0).getSide(); - if (side == 0) { - commentsForBase.putAll(psId, result); - } else { - commentsForPs.putAll(psId, result); - } + comments.putAll(new RevId(note.name()), result); } return noteMap; } @@ -152,10 +149,6 @@ return dateFormatter.formatDate(newIdent); } - public static PatchSet.Id getCommentPsId(PatchLineComment plc) { - return plc.getKey().getParentKey().getParentKey(); - } - private static PatchLineComment parseComment(byte[] note, MutableInteger curr, String currentFileName, PatchSet.Id psId, RevId revId, boolean isForBase, Charset enc, Status status) @@ -449,7 +442,7 @@ PatchLineComment first = comments.get(0); short side = first.getSide(); - PatchSet.Id psId = getCommentPsId(first); + PatchSet.Id psId = PatchLineCommentsUtil.getCommentPsId(first); appendHeaderField(writer, side == 0 ? BASE_PATCH_SET : PATCH_SET, @@ -459,7 +452,7 @@ String currentFilename = null; for (PatchLineComment c : comments) { - PatchSet.Id currentPsId = getCommentPsId(c); + PatchSet.Id currentPsId = PatchLineCommentsUtil.getCommentPsId(c); checkArgument(psId.equals(currentPsId), "All comments being added must all have the same PatchSet.Id. The" + "comment below does not have the same PatchSet.Id as the others " @@ -524,19 +517,47 @@ return buf.toByteArray(); } + /** + * Write comments for multiple revisions to a note map. + * <p> + * Mutates the map in-place. only notes for SHA-1s found as keys in the map + * are modified; all other notes are left untouched. + * + * @param noteMap note map to modify. + * @param allComments map of revision to all comments for that revision; + * callers are responsible for reading the original comments and applying + * any changes. Differs from a multimap in that present-but-empty values + * are significant, and indicate the note for that SHA-1 should be + * deleted. + * @param inserter object inserter for writing notes. + * @throws IOException if an error occurred. + */ public void writeCommentsToNoteMap(NoteMap noteMap, - List<PatchLineComment> allComments, ObjectInserter inserter) - throws IOException { - checkArgument(!allComments.isEmpty(), - "No comments to write; to delete, use removeNoteFromNoteMap()."); - ObjectId commit = - ObjectId.fromString(allComments.get(0).getRevId().get()); - Collections.sort(allComments, ChangeNotes.PLC_ORDER); - noteMap.set(commit, inserter.insert(OBJ_BLOB, buildNote(allComments))); + Map<RevId, List<PatchLineComment>> allComments, ObjectInserter inserter) + throws IOException { + for (Map.Entry<RevId, List<PatchLineComment>> e : allComments.entrySet()) { + List<PatchLineComment> comments = e.getValue(); + ObjectId commit = ObjectId.fromString(e.getKey().get()); + if (comments.isEmpty()) { + noteMap.remove(commit); + continue; + } + Collections.sort(comments, PLC_ORDER); + // We allow comments for multiple commits to be written in the same + // update, even though the rest of the metadata update is associated with + // a single patch set. + noteMap.set(commit, inserter.insert(OBJ_BLOB, buildNote(comments))); + } } - public void removeNote(NoteMap noteMap, RevId commitId) - throws IOException { - noteMap.remove(ObjectId.fromString(commitId.get())); + static void addCommentToMap(Map<RevId, List<PatchLineComment>> map, + PatchLineComment c) { + List<PatchLineComment> list = map.get(c.getRevId()); + if (list == null) { + list = new ArrayList<>(); + map.put(c.getRevId(), list); + } + list.add(c); } + }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java index 20d6c4a..a02c24d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -14,18 +14,14 @@ package com.google.gerrit.server.notedb; -import static com.google.gerrit.server.notedb.CommentsInNotesUtil.getCommentPsId; - import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Multimap; -import com.google.common.collect.Table; +import com.google.common.collect.ImmutableListMultimap; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchLineComment; -import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.git.GitRepositoryManager; @@ -70,8 +66,7 @@ private final AllUsersName draftsProject; private final Account.Id author; - private final Table<PatchSet.Id, String, PatchLineComment> draftBaseComments; - private final Table<PatchSet.Id, String, PatchLineComment> draftPsComments; + private ImmutableListMultimap<RevId, PatchLineComment> comments; private NoteMap noteMap; DraftCommentNotes(GitRepositoryManager repoManager, NotesMigration migration, @@ -79,9 +74,6 @@ super(repoManager, migration, changeId); this.draftsProject = draftsProject; this.author = author; - - this.draftBaseComments = HashBasedTable.create(); - this.draftPsComments = HashBasedTable.create(); } public NoteMap getNoteMap() { @@ -92,32 +84,18 @@ return author; } - /** - * @return a defensive copy of the table containing all draft comments - * on this change with side == 0. The row key is the comment's PatchSet.Id, - * the column key is the comment's UUID, and the value is the comment. - */ - public Table<PatchSet.Id, String, PatchLineComment> - getDraftBaseComments() { - return HashBasedTable.create(draftBaseComments); - } - - /** - * @return a defensive copy of the table containing all draft comments - * on this change with side == 1. The row key is the comment's PatchSet.Id, - * the column key is the comment's UUID, and the value is the comment. - */ - public Table<PatchSet.Id, String, PatchLineComment> - getDraftPsComments() { - return HashBasedTable.create(draftPsComments); + public ImmutableListMultimap<RevId, PatchLineComment> getComments() { + // TODO(dborowitz): Defensive copy? + return comments; } public boolean containsComment(PatchLineComment c) { - Table<PatchSet.Id, String, PatchLineComment> t = - c.getSide() == (short) 0 - ? getDraftBaseComments() - : getDraftPsComments(); - return t.contains(getCommentPsId(c), c.getKey().get()); + for (PatchLineComment existing : comments.values()) { + if (c.getKey().equals(existing.getKey())) { + return true; + } + } + return false; } @Override @@ -129,6 +107,7 @@ protected void onLoad() throws IOException, ConfigInvalidException { ObjectId rev = getRevision(); if (rev == null) { + loadDefaults(); return; } @@ -137,8 +116,7 @@ getChangeId(), walk, rev, repoManager, draftsProject, author)) { parser.parseDraftComments(); - buildCommentTable(draftBaseComments, parser.draftBaseComments); - buildCommentTable(draftPsComments, parser.draftPsComments); + comments = ImmutableListMultimap.copyOf(parser.comments); noteMap = parser.noteMap; } } @@ -152,20 +130,11 @@ @Override protected void loadDefaults() { - // Do nothing; tables are final and initialized in constructor. + comments = ImmutableListMultimap.of(); } @Override protected Project.NameKey getProjectName() { return draftsProject; } - - private void buildCommentTable( - Table<PatchSet.Id, String, PatchLineComment> commentTable, - Multimap<PatchSet.Id, PatchLineComment> allComments) { - for (PatchLineComment c : allComments.values()) { - commentTable.put(getCommentPsId(c), c.getKey().get(), c); - } - } - }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotesParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotesParser.java index 4b3fbdf..ef8683f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotesParser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotesParser.java
@@ -19,8 +19,8 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchLineComment; -import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.RefNames; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.git.GitRepositoryManager; @@ -34,8 +34,7 @@ import java.io.IOException; class DraftCommentNotesParser implements AutoCloseable { - final Multimap<PatchSet.Id, PatchLineComment> draftBaseComments; - final Multimap<PatchSet.Id, PatchLineComment> draftPsComments; + final Multimap<RevId, PatchLineComment> comments; NoteMap noteMap; private final Change.Id changeId; @@ -53,8 +52,7 @@ this.repo = repoManager.openMetadataRepository(draftsProject); this.author = author; - draftBaseComments = ArrayListMultimap.create(); - draftPsComments = ArrayListMultimap.create(); + comments = ArrayListMultimap.create(); } @Override @@ -66,7 +64,6 @@ walk.markStart(walk.parseCommit(tip)); noteMap = CommentsInNotesUtil.parseCommentsFromNotes(repo, RefNames.refsDraftComments(author, changeId), - walk, changeId, draftBaseComments, - draftPsComments, PatchLineComment.Status.DRAFT); + walk, changeId, comments, PatchLineComment.Status.DRAFT); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java index 40a51aa..b8caa62 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -87,6 +87,7 @@ private final ExecutorService diffExecutor; private final long timeoutMillis; + @Inject PatchListLoader(GitRepositoryManager mgr, PatchListCache plc, @@ -337,7 +338,7 @@ } @Override - public void release() { + public void close() { } });
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java index ea427eb..5b70730 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -177,7 +177,7 @@ boolean hugeFile = false; if (a.mode == FileMode.GITLINK || b.mode == FileMode.GITLINK) { - + // Do nothing } else if (a.src == b.src && a.size() <= context && content.getEdits().isEmpty()) { // Odd special case; the files are identical (100% rename or copy) @@ -214,7 +214,9 @@ a.displayMethod, b.displayMethod, a.mimeType.toString(), b.mimeType.toString(), comments, history, hugeFile, intralineDifferenceIsPossible, intralineFailure, intralineTimeout, - content.getPatchType() == Patch.PatchType.BINARY); + content.getPatchType() == Patch.PatchType.BINARY, + aId == null ? null : aId.getName(), + bId == null ? null : bId.getName()); } private static boolean isModify(PatchListEntry content) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java index 593f2c9..9827812 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java
@@ -14,17 +14,17 @@ package com.google.gerrit.server.plugins; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.jar.JarFile; class CleanupHandle { - private final File tmpFile; + private final Path tmp; private final JarFile jarFile; - CleanupHandle(File tmpFile, - JarFile jarFile) { - this.tmpFile = tmpFile; + CleanupHandle(Path tmp, JarFile jarFile) { + this.tmp = tmp; this.jarFile = jarFile; } @@ -34,12 +34,13 @@ } catch (IOException err) { PluginLoader.log.error("Cannot close " + jarFile.getName(), err); } - if (!tmpFile.delete() && tmpFile.exists()) { - PluginLoader.log.warn("Cannot delete " + tmpFile.getAbsolutePath() - + ", retrying to delete it on termination of the virtual machine"); - tmpFile.deleteOnExit(); - } else { - PluginLoader.log.info("Cleaned plugin " + tmpFile.getName()); + try { + Files.deleteIfExists(tmp); + PluginLoader.log.info("Cleaned plugin " + tmp.getFileName()); + } catch (IOException e) { + PluginLoader.log.warn("Cannot delete " + tmp.toAbsolutePath() + + ", retrying to delete it on termination of the virtual machine", e); + tmp.toFile().deleteOnExit(); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java index 7252617..1d4233a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java
@@ -33,7 +33,7 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.PersonIdent; -import java.io.File; +import java.nio.file.Path; /** * Copies critical objects from the {@code dbInjector} into a plugin. @@ -47,11 +47,11 @@ class CopyConfigModule extends AbstractModule { @Inject @SitePath - private File sitePath; + private Path sitePath; @Provides @SitePath - File getSitePath() { + Path getSitePath() { return sitePath; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java index 3406080..926ef44 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -24,13 +24,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -43,7 +44,7 @@ static final String JAR_EXTENSION = ".jar"; static final Logger log = LoggerFactory.getLogger(JarPluginProvider.class); - private final File tmpDir; + private final Path tmpDir; @Inject JarPluginProvider(SitePaths sitePaths) { @@ -51,42 +52,42 @@ } @Override - public boolean handles(File srcFile) { - String fileName = srcFile.getName(); + public boolean handles(Path srcPath) { + String fileName = srcPath.getFileName().toString(); return fileName.endsWith(JAR_EXTENSION) || fileName.endsWith(JAR_EXTENSION + ".disabled"); } @Override - public String getPluginName(File srcFile) { + public String getPluginName(Path srcPath) { try { - return MoreObjects.firstNonNull(getJarPluginName(srcFile), - PluginLoader.nameOf(srcFile)); + return MoreObjects.firstNonNull(getJarPluginName(srcPath), + PluginLoader.nameOf(srcPath)); } catch (IOException e) { - throw new IllegalArgumentException("Invalid plugin file " + srcFile + throw new IllegalArgumentException("Invalid plugin file " + srcPath + ": cannot get plugin name", e); } } - public static String getJarPluginName(File srcFile) throws IOException { - try (JarFile jarFile = new JarFile(srcFile)) { + public static String getJarPluginName(Path srcPath) throws IOException { + try (JarFile jarFile = new JarFile(srcPath.toFile())) { return jarFile.getManifest().getMainAttributes() .getValue("Gerrit-PluginName"); } } @Override - public ServerPlugin get(File srcFile, FileSnapshot snapshot, + public ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription description) throws InvalidPluginException { try { - String name = getPluginName(srcFile); - String extension = getExtension(srcFile); - try (FileInputStream in = new FileInputStream(srcFile)) { - File tmp = asTemp(in, tempNameFor(name), extension, tmpDir); - return loadJarPlugin(name, srcFile, snapshot, tmp, description); + String name = getPluginName(srcPath); + String extension = getExtension(srcPath); + try (InputStream in = Files.newInputStream(srcPath)) { + Path tmp = asTemp(in, tempNameFor(name), extension, tmpDir); + return loadJarPlugin(name, srcPath, snapshot, tmp, description); } } catch (IOException e) { - throw new InvalidPluginException("Cannot load Jar plugin " + srcFile, e); + throw new InvalidPluginException("Cannot load Jar plugin " + srcPath, e); } } @@ -95,8 +96,8 @@ return "gerrit"; } - private static String getExtension(File file) { - return getExtension(file.getName()); + private static String getExtension(Path path) { + return getExtension(path.getFileName().toString()); } private static String getExtension(String name) { @@ -109,18 +110,18 @@ return PLUGIN_TMP_PREFIX + name + "_" + fmt.format(new Date()) + "_"; } - public static File storeInTemp(String pluginName, InputStream in, + public static Path storeInTemp(String pluginName, InputStream in, SitePaths sitePaths) throws IOException { - if (!sitePaths.tmp_dir.exists()) { - sitePaths.tmp_dir.mkdirs(); + if (!Files.exists(sitePaths.tmp_dir)) { + Files.createDirectories(sitePaths.tmp_dir); } return asTemp(in, tempNameFor(pluginName), ".jar", sitePaths.tmp_dir); } - private ServerPlugin loadJarPlugin(String name, File srcJar, - FileSnapshot snapshot, File tmp, PluginDescription description) + private ServerPlugin loadJarPlugin(String name, Path srcJar, + FileSnapshot snapshot, Path tmp, PluginDescription description) throws IOException, InvalidPluginException, MalformedURLException { - JarFile jarFile = new JarFile(tmp); + JarFile jarFile = new JarFile(tmp.toFile()); boolean keep = false; try { Manifest manifest = jarFile.getManifest(); @@ -129,24 +130,22 @@ List<URL> urls = new ArrayList<>(2); String overlay = System.getProperty("gerrit.plugin-classes"); if (overlay != null) { - File classes = new File(new File(new File(overlay), name), "main"); - if (classes.isDirectory()) { - log.info(String.format("plugin %s: including %s", name, - classes.getPath())); - urls.add(classes.toURI().toURL()); + Path classes = Paths.get(overlay).resolve(name).resolve("main"); + if (Files.isDirectory(classes)) { + log.info(String.format("plugin %s: including %s", name, classes)); + urls.add(classes.toUri().toURL()); } } - urls.add(tmp.toURI().toURL()); + urls.add(tmp.toUri().toURL()); ClassLoader pluginLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), PluginLoader.parentFor(type)); JarScanner jarScanner = createJarScanner(tmp); - ServerPlugin plugin = - new ServerPlugin(name, description.canonicalUrl, description.user, - srcJar, snapshot, jarScanner, description.dataDir, - pluginLoader); + ServerPlugin plugin = new ServerPlugin(name, description.canonicalUrl, + description.user, srcJar, snapshot, jarScanner, + description.dataDir, pluginLoader); plugin.setCleanupHandle(new CleanupHandle(tmp, jarFile)); keep = true; return plugin; @@ -157,7 +156,7 @@ } } - private JarScanner createJarScanner(File srcJar) + private JarScanner createJarScanner(Path srcJar) throws InvalidPluginException { try { return new JarScanner(srcJar);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java index a8600fe..0f4aa6c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -39,10 +39,10 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -69,8 +69,8 @@ private final JarFile jarFile; - public JarScanner(File srcFile) throws IOException { - this.jarFile = new JarFile(srcFile); + public JarScanner(Path src) throws IOException { + this.jarFile = new JarFile(src.toFile()); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java index 63f69b5..8da8cc1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java
@@ -27,12 +27,12 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; class JsPlugin extends Plugin { private Injector httpInjector; - JsPlugin(String name, File srcFile, PluginUser pluginUser, + JsPlugin(String name, Path srcFile, PluginUser pluginUser, FileSnapshot snapshot) { super(name, srcFile, pluginUser, snapshot, ApiType.JS); } @@ -40,7 +40,7 @@ @Override @Nullable public String getVersion() { - String fileName = getSrcFile().getName(); + String fileName = getSrcFile().getFileName().toString(); int firstDash = fileName.indexOf("-"); if (firstDash > 0) { return fileName.substring(firstDash + 1, fileName.lastIndexOf(".js")); @@ -51,7 +51,7 @@ @Override public void start(PluginGuiceEnvironment env) throws Exception { manager = new LifecycleManager(); - String fileName = getSrcFile().getName(); + String fileName = getSrcFile().getFileName().toString(); httpInjector = Guice.createInjector(new StandaloneJsPluginModule(getName(), fileName)); manager.start();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java index 54f05f0..1d717ef 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -90,7 +90,7 @@ stdout.format("%-30s %-10s %-8s %s\n", p.getName(), Strings.nullToEmpty(info.version), p.isDisabled() ? "DISABLED" : "ENABLED", - p.getSrcFile().getName()); + p.getSrcFile().getFileName()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java index 82a6ad9..cf38310 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java
@@ -18,14 +18,14 @@ import com.google.common.base.Joiner; import com.google.common.collect.Iterables; -import java.io.File; +import java.nio.file.Path; class MultipleProvidersForPluginException extends IllegalArgumentException { private static final long serialVersionUID = 1L; - MultipleProvidersForPluginException(File pluginSrcFile, + MultipleProvidersForPluginException(Path pluginSrcPath, Iterable<ServerPluginProvider> providersHandlers) { - super(pluginSrcFile.getAbsolutePath() + super(pluginSrcPath.toAbsolutePath() + " is claimed to be handled by more than one plugin provider: " + providersListToString(providersHandlers)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java index b227909..6b84c21 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
@@ -14,6 +14,8 @@ package com.google.gerrit.server.plugins; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; @@ -25,7 +27,7 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.jar.Attributes; @@ -67,7 +69,7 @@ } private final String name; - private final File srcFile; + private final Path srcFile; private final ApiType apiType; private final boolean disabled; private final CacheKey cacheKey; @@ -80,17 +82,17 @@ private List<ReloadableRegistrationHandle<?>> reloadableHandles; public Plugin(String name, - File srcFile, + Path srcPath, PluginUser pluginUser, FileSnapshot snapshot, ApiType apiType) { this.name = name; - this.srcFile = srcFile; + this.srcFile = srcPath; this.apiType = apiType; this.snapshot = snapshot; this.pluginUser = pluginUser; this.cacheKey = new Plugin.CacheKey(name); - this.disabled = srcFile.getName().endsWith(".disabled"); + this.disabled = srcPath.getFileName().toString().endsWith(".disabled"); } public CleanupHandle getCleanupHandle() { @@ -105,7 +107,7 @@ return pluginUser; } - public File getSrcFile() { + public Path getSrcFile() { return srcFile; } @@ -168,7 +170,7 @@ abstract boolean canReload(); - boolean isModified(File jar) { - return snapshot.lastModified() != jar.lastModified(); + boolean isModified(Path jar) { + return snapshot.lastModified() != lastModified(jar); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java index 0228509..1d9cd0e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java
@@ -11,14 +11,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package com.google.gerrit.server.plugins; import com.google.common.base.Optional; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.nio.file.NoSuchFileException; import java.util.Collections; import java.util.Enumeration; import java.util.Map; @@ -57,7 +58,7 @@ @Override public InputStream getInputStream(PluginEntry entry) throws IOException { - throw new FileNotFoundException("Empty plugin"); + throw new NoSuchFileException("Empty plugin"); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginEntry.java index 7242e98..74ded73 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginEntry.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginEntry.java
@@ -23,7 +23,7 @@ * Plugin static resource entry * * Bean representing a static resource inside a plugin. - * All static resources are available at <plugin web url>/static + * All static resources are available at {@code <plugin web url>/static} * and served by the HttpPluginServlet. */ public class PluginEntry {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java index 3fbbfa9..2887a00 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
@@ -518,10 +518,12 @@ bindings.remove(Key.get(Injector.class)); bindings.remove(Key.get(java.util.logging.Logger.class)); - final @Nullable Binding<HttpServletRequest> requestBinding = + @Nullable + final Binding<HttpServletRequest> requestBinding = src.getExistingBinding(Key.get(HttpServletRequest.class)); - final @Nullable Binding<HttpServletResponse> responseBinding = + @Nullable + final Binding<HttpServletResponse> responseBinding = src.getExistingBinding(Key.get(HttpServletResponse.class)); return new AbstractModule() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index b51359d..4e651c2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -18,6 +18,8 @@ import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; @@ -25,6 +27,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Queues; import com.google.common.collect.Sets; +import com.google.common.io.ByteStreams; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.systemstatus.ServerInformation; @@ -45,14 +48,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileFilter; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -71,13 +75,13 @@ static final String PLUGIN_TMP_PREFIX = "plugin_"; static final Logger log = LoggerFactory.getLogger(PluginLoader.class); - public String getPluginName(File srcFile) { - return MoreObjects.firstNonNull(getGerritPluginName(srcFile), - nameOf(srcFile)); + public String getPluginName(Path srcPath) { + return MoreObjects.firstNonNull(getGerritPluginName(srcPath), + nameOf(srcPath)); } - private final File pluginsDir; - private final File dataDir; + private final Path pluginsDir; + private final Path dataDir; private final PluginGuiceEnvironment env; private final ServerInformationImpl srvInfoImpl; private final PluginUser.Factory pluginUserFactory; @@ -158,7 +162,7 @@ checkRemoteInstall(); String fileName = originalName; - File tmp = asTemp(in, ".next_" + fileName + "_", ".tmp", pluginsDir); + Path tmp = asTemp(in, ".next_" + fileName + "_", ".tmp", pluginsDir); String name = MoreObjects.firstNonNull(getGerritPluginName(tmp), nameOf(fileName)); if (!originalName.equals(name)) { @@ -168,26 +172,26 @@ } String fileExtension = getExtension(fileName); - File dst = new File(pluginsDir, name + fileExtension); + Path dst = pluginsDir.resolve(name + fileExtension); synchronized (this) { Plugin active = running.get(name); if (active != null) { - fileName = active.getSrcFile().getName(); + fileName = active.getSrcFile().getFileName().toString(); log.info(String.format("Replacing plugin %s", active.getName())); - File old = new File(pluginsDir, ".last_" + fileName); - old.delete(); - active.getSrcFile().renameTo(old); + Path old = pluginsDir.resolve(".last_" + fileName); + Files.deleteIfExists(old); + Files.move(active.getSrcFile(), old); } - new File(pluginsDir, fileName + ".disabled").delete(); - tmp.renameTo(dst); + Files.deleteIfExists(pluginsDir.resolve(fileName + ".disabled")); + Files.move(tmp, dst); try { Plugin plugin = runPlugin(name, dst, active); if (active == null) { log.info(String.format("Installed plugin %s", plugin.getName())); } } catch (PluginInstallException e) { - dst.delete(); + Files.deleteIfExists(dst); throw e; } @@ -195,21 +199,17 @@ } } - static File asTemp(InputStream in, String prefix, String suffix, File dir) + static Path asTemp(InputStream in, String prefix, String suffix, Path dir) throws IOException { - File tmp = File.createTempFile(prefix, suffix, dir); + Path tmp = Files.createTempFile(dir, prefix, suffix); boolean keep = false; - try (FileOutputStream out = new FileOutputStream(tmp)) { - byte[] data = new byte[8192]; - int n; - while ((n = in.read(data)) > 0) { - out.write(data, 0, n); - } + try (OutputStream out = Files.newOutputStream(tmp)) { + ByteStreams.copy(in, out); keep = true; return tmp; } finally { if (!keep) { - tmp.delete(); + Files.delete(tmp); } } } @@ -217,7 +217,8 @@ private synchronized void unloadPlugin(Plugin plugin) { persistentCacheFactory.onStop(plugin); String name = plugin.getName(); - log.info(String.format("Unloading plugin %s", name)); + log.info(String.format("Unloading plugin %s, version %s", + name, plugin.getVersion())); plugin.stop(env); env.onStopPlugin(plugin); running.remove(name); @@ -240,12 +241,21 @@ } log.info(String.format("Disabling plugin %s", active.getName())); - File off = new File(active.getSrcFile() + ".disabled"); - active.getSrcFile().renameTo(off); + Path off = active.getSrcFile().resolveSibling( + active.getSrcFile().getFileName() + ".disabled"); + try { + Files.move(active.getSrcFile(), off); + } catch (IOException e) { + log.error("Failed to disable plugin", e); + // In theory we could still unload the plugin even if the rename + // failed. However, it would be reloaded on the next server startup, + // which is probably not what the user expects. + continue; + } unloadPlugin(active); try { - FileSnapshot snapshot = FileSnapshot.save(off); + FileSnapshot snapshot = FileSnapshot.save(off.toFile()); Plugin offPlugin = loadPlugin(name, off, snapshot); disabled.put(name, offPlugin); } catch (Throwable e) { @@ -274,13 +284,17 @@ } log.info(String.format("Enabling plugin %s", name)); - String n = off.getSrcFile().getName(); + String n = off.getSrcFile().toFile().getName(); if (n.endsWith(".disabled")) { n = n.substring(0, n.lastIndexOf('.')); } - File on = new File(pluginsDir, n); - off.getSrcFile().renameTo(on); - + Path on = pluginsDir.resolve(n); + try { + Files.move(off.getSrcFile(), on); + } catch (IOException e) { + log.error("Failed to move plugin " + name + " into place", e); + continue; + } disabled.remove(name); runPlugin(name, on, null); } @@ -290,7 +304,7 @@ @Override public synchronized void start() { - log.info("Loading plugins from " + pluginsDir.getAbsolutePath()); + log.info("Loading plugins from " + pluginsDir.toAbsolutePath()); srvInfoImpl.state = ServerInformation.State.STARTUP; rescan(); srvInfoImpl.state = ServerInformation.State.RUNNING; @@ -342,7 +356,9 @@ String name = active.getName(); try { log.info(String.format("Reloading plugin %s", name)); - runPlugin(name, active.getSrcFile(), active); + Plugin newPlugin = runPlugin(name, active.getSrcFile(), active); + log.info(String.format("Reloaded plugin %s, version %s", + newPlugin.getName(), newPlugin.getVersion())); } catch (PluginInstallException e) { log.warn(String.format("Cannot reload plugin %s", name), e.getCause()); throw e; @@ -354,42 +370,42 @@ } public synchronized void rescan() { - Multimap<String, File> pluginsFiles = prunePlugins(pluginsDir); + Multimap<String, Path> pluginsFiles = prunePlugins(pluginsDir); if (pluginsFiles.isEmpty()) { return; } syncDisabledPlugins(pluginsFiles); - Map<String, File> activePlugins = filterDisabled(pluginsFiles); - for (Map.Entry<String, File> entry : jarsFirstSortedPluginsSet(activePlugins)) { + Map<String, Path> activePlugins = filterDisabled(pluginsFiles); + for (Map.Entry<String, Path> entry : jarsFirstSortedPluginsSet(activePlugins)) { String name = entry.getKey(); - File file = entry.getValue(); - String fileName = file.getName(); - if (!isJsPlugin(fileName) && !serverPluginFactory.handles(file)) { + Path path = entry.getValue(); + String fileName = path.getFileName().toString(); + if (!isJsPlugin(fileName) && !serverPluginFactory.handles(path)) { log.warn("No Plugin provider was found that handles this file format: {}", fileName); continue; } FileSnapshot brokenTime = broken.get(name); - if (brokenTime != null && !brokenTime.isModified(file)) { + if (brokenTime != null && !brokenTime.isModified(path.toFile())) { continue; } Plugin active = running.get(name); - if (active != null && !active.isModified(file)) { + if (active != null && !active.isModified(path)) { continue; } if (active != null) { - log.info(String.format("Reloading plugin %s, version %s", - active.getName(), active.getVersion())); + log.info(String.format("Reloading plugin %s", active.getName())); } try { - Plugin loadedPlugin = runPlugin(name, file, active); - if (active == null && !loadedPlugin.isDisabled()) { - log.info(String.format("Loaded plugin %s, version %s", + Plugin loadedPlugin = runPlugin(name, path, active); + if (!loadedPlugin.isDisabled()) { + log.info(String.format("%s plugin %s, version %s", + active == null ? "Loaded" : "Reloaded", loadedPlugin.getName(), loadedPlugin.getVersion())); } } catch (PluginInstallException e) { @@ -400,31 +416,32 @@ cleanInBackground(); } - private void addAllEntries(Map<String, File> from, - TreeSet<Entry<String, File>> to) { - Iterator<Entry<String, File>> it = from.entrySet().iterator(); + private void addAllEntries(Map<String, Path> from, + TreeSet<Entry<String, Path>> to) { + Iterator<Entry<String, Path>> it = from.entrySet().iterator(); while (it.hasNext()) { - Entry<String,File> entry = it.next(); + Entry<String,Path> entry = it.next(); to.add(new AbstractMap.SimpleImmutableEntry<>( entry.getKey(), entry.getValue())); } } - private TreeSet<Entry<String, File>> jarsFirstSortedPluginsSet( - Map<String, File> activePlugins) { - TreeSet<Entry<String, File>> sortedPlugins = - Sets.newTreeSet(new Comparator<Entry<String, File>>() { + private TreeSet<Entry<String, Path>> jarsFirstSortedPluginsSet( + Map<String, Path> activePlugins) { + TreeSet<Entry<String, Path>> sortedPlugins = + Sets.newTreeSet(new Comparator<Entry<String, Path>>() { @Override - public int compare(Entry<String, File> entry1, - Entry<String, File> entry2) { - String file1 = entry1.getValue().getName(); - String file2 = entry2.getValue().getName(); - int cmp = file1.compareTo(file2); - if (file1.endsWith(".jar")) { - return (file2.endsWith(".jar") ? cmp : -1); - } else { - return (file2.endsWith(".jar") ? +1 : cmp); - } + public int compare(Entry<String, Path> e1, Entry<String, Path> e2) { + Path n1 = e1.getValue().getFileName(); + Path n2 = e2.getValue().getFileName(); + return ComparisonChain.start() + .compareTrueFirst(isJar(n1), isJar(n2)) + .compare(n1, n2) + .result(); + } + + private boolean isJar(Path n1) { + return n1.toString().endsWith(".jar"); } }); @@ -432,14 +449,14 @@ return sortedPlugins; } - private void syncDisabledPlugins(Multimap<String, File> jars) { + private void syncDisabledPlugins(Multimap<String, Path> jars) { stopRemovedPlugins(jars); dropRemovedDisabledPlugins(jars); } - private Plugin runPlugin(String name, File plugin, Plugin oldPlugin) + private Plugin runPlugin(String name, Path plugin, Plugin oldPlugin) throws PluginInstallException { - FileSnapshot snapshot = FileSnapshot.save(plugin); + FileSnapshot snapshot = FileSnapshot.save(plugin.toFile()); try { Plugin newPlugin = loadPlugin(name, plugin, snapshot); if (newPlugin.getCleanupHandle() != null) { @@ -479,11 +496,11 @@ } } - private void stopRemovedPlugins(Multimap<String, File> jars) { + private void stopRemovedPlugins(Multimap<String, Path> jars) { Set<String> unload = Sets.newHashSet(running.keySet()); - for (Map.Entry<String, Collection<File>> entry : jars.asMap().entrySet()) { - for (File file : entry.getValue()) { - if (!file.getName().endsWith(".disabled")) { + for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) { + for (Path path : entry.getValue()) { + if (!path.getFileName().toString().endsWith(".disabled")) { unload.remove(entry.getKey()); } } @@ -493,11 +510,11 @@ } } - private void dropRemovedDisabledPlugins(Multimap<String, File> jars) { + private void dropRemovedDisabledPlugins(Multimap<String, Path> jars) { Set<String> unload = Sets.newHashSet(disabled.keySet()); - for (Map.Entry<String, Collection<File>> entry : jars.asMap().entrySet()) { - for (File file : entry.getValue()) { - if (file.getName().endsWith(".disabled")) { + for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) { + for (Path path : entry.getValue()) { + if (path.getFileName().toString().endsWith(".disabled")) { unload.remove(entry.getKey()); } } @@ -528,8 +545,8 @@ } } - public static String nameOf(File plugin) { - return nameOf(plugin.getName()); + public static String nameOf(Path plugin) { + return nameOf(plugin.getFileName().toString()); } private static String nameOf(String name) { @@ -545,21 +562,21 @@ return 0 < ext ? name.substring(ext) : ""; } - private Plugin loadPlugin(String name, File srcPlugin, FileSnapshot snapshot) + private Plugin loadPlugin(String name, Path srcPlugin, FileSnapshot snapshot) throws InvalidPluginException { - String pluginName = srcPlugin.getName(); + String pluginName = srcPlugin.getFileName().toString(); if (isJsPlugin(pluginName)) { return loadJsPlugin(name, srcPlugin, snapshot); } else if (serverPluginFactory.handles(srcPlugin)) { return loadServerPlugin(srcPlugin, snapshot); } else { throw new InvalidPluginException(String.format( - "Unsupported plugin type: %s", srcPlugin.getName())); + "Unsupported plugin type: %s", srcPlugin.getFileName())); } } - private File getPluginDataDir(String name) { - return new File(dataDir, name); + private Path getPluginDataDir(String name) { + return dataDir.resolve(name); } private String getPluginCanonicalWebUrl(String name) { @@ -569,11 +586,11 @@ return url; } - private Plugin loadJsPlugin(String name, File srcJar, FileSnapshot snapshot) { + private Plugin loadJsPlugin(String name, Path srcJar, FileSnapshot snapshot) { return new JsPlugin(name, srcJar, pluginUserFactory.create(name), snapshot); } - private ServerPlugin loadServerPlugin(File scriptFile, + private ServerPlugin loadServerPlugin(Path scriptFile, FileSnapshot snapshot) throws InvalidPluginException { String name = serverPluginFactory.getPluginName(scriptFile); return serverPluginFactory.get(scriptFile, snapshot, new PluginDescription( @@ -597,15 +614,15 @@ // Only one active plugin per plugin name can exist for each plugin name. // Filter out disabled plugins and transform the multimap to a map - private static Map<String, File> filterDisabled( - Multimap<String, File> pluginFiles) { - Map<String, File> activePlugins = Maps.newHashMapWithExpectedSize( - pluginFiles.keys().size()); - for (String name : pluginFiles.keys()) { - for (File pluginFile : pluginFiles.asMap().get(name)) { - if (!pluginFile.getName().endsWith(".disabled")) { + private static Map<String, Path> filterDisabled( + Multimap<String, Path> pluginPaths) { + Map<String, Path> activePlugins = Maps.newHashMapWithExpectedSize( + pluginPaths.keys().size()); + for (String name : pluginPaths.keys()) { + for (Path pluginPath : pluginPaths.asMap().get(name)) { + if (!pluginPath.getFileName().toString().endsWith(".disabled")) { assert(!activePlugins.containsKey(name)); - activePlugins.put(name, pluginFile); + activePlugins.put(name, pluginPath); } } } @@ -621,37 +638,40 @@ // // NOTE: Bear in mind that the plugin name can be reassigned after load by the // Server plugin provider. - public Multimap<String, File> prunePlugins(File pluginsDir) { - List<File> pluginFiles = scanFilesInPluginsDirectory(pluginsDir); - Multimap<String, File> map; - map = asMultimap(pluginFiles); + public Multimap<String, Path> prunePlugins(Path pluginsDir) { + List<Path> pluginPaths = scanPathsInPluginsDirectory(pluginsDir); + Multimap<String, Path> map; + map = asMultimap(pluginPaths); for (String plugin : map.keySet()) { - Collection<File> files = map.asMap().get(plugin); + Collection<Path> files = map.asMap().get(plugin); if (files.size() == 1) { continue; } // retrieve enabled plugins - Iterable<File> enabled = filterDisabledPlugins( - files); + Iterable<Path> enabled = filterDisabledPlugins(files); // If we have only one (the winner) plugin, nothing to do if (!Iterables.skip(enabled, 1).iterator().hasNext()) { continue; } - File winner = Iterables.getFirst(enabled, null); + Path winner = Iterables.getFirst(enabled, null); assert(winner != null); // Disable all loser plugins by renaming their file names to // "file.disabled" and replace the disabled files in the multimap. - Collection<File> elementsToRemove = Lists.newArrayList(); - Collection<File> elementsToAdd = Lists.newArrayList(); - for (File loser : Iterables.skip(enabled, 1)) { + Collection<Path> elementsToRemove = Lists.newArrayList(); + Collection<Path> elementsToAdd = Lists.newArrayList(); + for (Path loser : Iterables.skip(enabled, 1)) { log.warn(String.format("Plugin <%s> was disabled, because" + " another plugin <%s>" + " with the same name <%s> already exists", loser, winner, plugin)); - File disabledPlugin = new File(loser + ".disabled"); + Path disabledPlugin = Paths.get(loser + ".disabled"); elementsToAdd.add(disabledPlugin); elementsToRemove.add(loser); - loser.renameTo(disabledPlugin); + try { + Files.move(loser, disabledPlugin); + } catch (IOException e) { + log.warn("Failed to fully disable plugin " + loser, e); + } } Iterables.removeAll(files, elementsToRemove); Iterables.addAll(files, elementsToAdd); @@ -659,50 +679,52 @@ return map; } - private List<File> scanFilesInPluginsDirectory(File pluginsDir) { - if (pluginsDir == null || !pluginsDir.exists()) { + private List<Path> scanPathsInPluginsDirectory(Path pluginsDir) { + if (pluginsDir == null || !Files.exists(pluginsDir)) { return Collections.emptyList(); } - File[] matches = pluginsDir.listFiles(new FileFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File pathname) { - String n = pathname.getName(); + public boolean accept(Path entry) throws IOException { + String n = entry.getFileName().toString(); return !n.startsWith(".last_") && !n.startsWith(".next_"); } - }); - if (matches == null) { - log.error("Cannot list " + pluginsDir.getAbsolutePath()); - return Collections.emptyList(); + }; + try (DirectoryStream<Path> files + = Files.newDirectoryStream(pluginsDir, filter)) { + return ImmutableList.copyOf(files); + } catch (IOException e) { + log.error("Cannot list " + pluginsDir.toAbsolutePath(), e); + return ImmutableList.of(); } - return Arrays.asList(matches); } - private static Iterable<File> filterDisabledPlugins( - Collection<File> files) { - return Iterables.filter(files, new Predicate<File>() { + private static Iterable<Path> filterDisabledPlugins( + Collection<Path> paths) { + return Iterables.filter(paths, new Predicate<Path>() { @Override - public boolean apply(File file) { - return !file.getName().endsWith(".disabled"); + public boolean apply(Path p) { + return !p.getFileName().toString().endsWith(".disabled"); } }); } - public String getGerritPluginName(File srcFile) { - String fileName = srcFile.getName(); + public String getGerritPluginName(Path srcPath) { + String fileName = srcPath.getFileName().toString(); if (isJsPlugin(fileName)) { return fileName.substring(0, fileName.length() - 3); } - if (serverPluginFactory.handles(srcFile)) { - return serverPluginFactory.getPluginName(srcFile); + if (serverPluginFactory.handles(srcPath)) { + return serverPluginFactory.getPluginName(srcPath); } return null; } - private Multimap<String, File> asMultimap(List<File> plugins) { - Multimap<String, File> map = LinkedHashMultimap.create(); - for (File srcFile : plugins) { - map.put(getPluginName(srcFile), srcFile); + private Multimap<String, Path> asMultimap(List<Path> plugins) { + Multimap<String, Path> map = LinkedHashMultimap.create(); + for (Path srcPath : plugins) { + map.put(getPluginName(srcPath), srcPath); } return map; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java index 0b037fb..28d57b2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
@@ -17,25 +17,19 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; -import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl; -import com.google.gerrit.extensions.annotations.PluginData; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle; import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.server.PluginUser; import com.google.gerrit.server.util.RequestContext; -import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -59,7 +53,7 @@ private final Manifest manifest; private final PluginContentScanner scanner; - private final File dataDir; + private final Path dataDir; private final String pluginCanonicalWebUrl; private final ClassLoader classLoader; private Class<? extends Module> sysModule; @@ -75,12 +69,13 @@ public ServerPlugin(String name, String pluginCanonicalWebUrl, PluginUser pluginUser, - File srcJar, + Path srcJar, FileSnapshot snapshot, PluginContentScanner scanner, - File dataDir, + Path dataDir, ClassLoader classLoader) throws InvalidPluginException { - super(name, srcJar, pluginUser, snapshot, Plugin.getApiType(getPluginManifest(scanner))); + super(name, srcJar, pluginUser, snapshot, + Plugin.getApiType(getPluginManifest(scanner))); this.pluginCanonicalWebUrl = pluginCanonicalWebUrl; this.scanner = scanner; this.dataDir = dataDir; @@ -127,10 +122,18 @@ return (Class<? extends Module>) clazz; } - File getSrcJar() { + Path getSrcJar() { return getSrcFile(); } + Path getDataDir() { + return dataDir; + } + + String getPluginCanonicalWebUrl() { + return pluginCanonicalWebUrl; + } + private static Manifest getPluginManifest(PluginContentScanner scanner) throws InvalidPluginException { try { @@ -229,45 +232,11 @@ } private Injector newRootInjector(final PluginGuiceEnvironment env) { - List<Module> modules = Lists.newArrayListWithCapacity(4); + List<Module> modules = Lists.newArrayListWithCapacity(2); if (getApiType() == ApiType.PLUGIN) { modules.add(env.getSysModule()); } - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(PluginUser.class).toInstance(getPluginUser()); - bind(String.class) - .annotatedWith(PluginName.class) - .toInstance(getName()); - bind(String.class) - .annotatedWith(PluginCanonicalWebUrl.class) - .toInstance(pluginCanonicalWebUrl); - - bind(File.class) - .annotatedWith(PluginData.class) - .toProvider(new Provider<File>() { - private volatile boolean ready; - - @Override - public File get() { - if (!ready) { - synchronized (dataDir) { - if (!ready) { - if (!dataDir.exists() && !dataDir.mkdirs()) { - throw new ProvisionException(String.format( - "Cannot create %s for plugin %s", - dataDir.getAbsolutePath(), getName())); - } - ready = true; - } - } - } - return dataDir; - } - }); - } - }); + modules.add(new ServerPluginInfoModule(this)); return Guice.createInjector(modules); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java new file mode 100644 index 0000000..b0e9453 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
@@ -0,0 +1,77 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.plugins; + +import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl; +import com.google.gerrit.extensions.annotations.PluginData; +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.PluginUser; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.ProvisionException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +class ServerPluginInfoModule extends AbstractModule { + private final ServerPlugin plugin; + private final Path dataDir; + + private volatile boolean ready; + + ServerPluginInfoModule(ServerPlugin plugin) { + this.plugin = plugin; + this.dataDir = plugin.getDataDir(); + } + + @Override + protected void configure() { + bind(PluginUser.class).toInstance(plugin.getPluginUser()); + bind(String.class) + .annotatedWith(PluginName.class) + .toInstance(plugin.getName()); + bind(String.class) + .annotatedWith(PluginCanonicalWebUrl.class) + .toInstance(plugin.getPluginCanonicalWebUrl()); + } + + @Provides + @PluginData + Path getPluginData() { + if (!ready) { + synchronized (dataDir) { + if (!ready) { + try { + Files.createDirectories(dataDir); + } catch (IOException e) { + throw new ProvisionException(String.format( + "Cannot create %s for plugin %s", + dataDir.toAbsolutePath(), plugin.getName()), e); + } + ready = true; + } + } + } + return dataDir; + } + + @Provides + @PluginData + File getPluginDataAsFile(@PluginData Path pluginData) { + return pluginData.toFile(); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java index 37fed9b..bc2432b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java
@@ -19,7 +19,7 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; /** * Provider of one Server plugin from one external file @@ -40,7 +40,7 @@ public class PluginDescription { public final PluginUser user; public final String canonicalUrl; - public final File dataDir; + public final Path dataDir; /** * Creates a new PluginDescription for ServerPluginProvider. @@ -49,7 +49,7 @@ * @param canonicalUrl plugin root Web URL * @param dataDir directory for plugin data */ - public PluginDescription(PluginUser user, String canonicalUrl, File dataDir) { + public PluginDescription(PluginUser user, String canonicalUrl, Path dataDir) { this.user = user; this.canonicalUrl = canonicalUrl; this.dataDir = dataDir; @@ -59,39 +59,39 @@ /** * Declares the availability to manage an external file or directory * - * @param srcFile the external file or directory + * @param srcPath the external file or directory * @return true if file or directory can be loaded into a Server Plugin */ - boolean handles(File srcFile); + boolean handles(Path srcPath); /** * Returns the plugin name of an external file or directory * - * Should be called only if {@link #handles(File) handles(srcFile)} + * Should be called only if {@link #handles(Path) handles(srcFile)} * returns true and thus srcFile is a supported plugin format. * An IllegalArgumentException is thrown otherwise as srcFile * is not a valid file format for extracting its plugin name. * - * @param srcFile external file or directory + * @param srcPath external file or directory * @return plugin name */ - String getPluginName(File srcFile); + String getPluginName(Path srcPath); /** * Loads an external file or directory into a Server plugin. * - * Should be called only if {@link #handles(File) handles(srcFile)} + * Should be called only if {@link #handles(Path) handles(srcFile)} * returns true and thus srcFile is a supported plugin format. * An IllegalArgumentException is thrown otherwise as srcFile * is not a valid file format for extracting its plugin name. * - * @param srcFile external file or directory + * @param srcPath external file or directory * @param snapshot snapshot of the external file * @param pluginDescriptor descriptor of the ServerPlugin to load * @throws InvalidPluginException if plugin is supposed to be handled * but cannot be loaded for any other reason */ - ServerPlugin get(File srcFile, FileSnapshot snapshot, + ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription pluginDescriptor) throws InvalidPluginException; /**
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java index 0e8bd87..afdc5b3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
@@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -38,27 +38,26 @@ } @Override - public ServerPlugin get(File srcFile, FileSnapshot snapshot, + public ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription pluginDescription) throws InvalidPluginException { - return providerOf(srcFile).get(srcFile, snapshot, pluginDescription); + return providerOf(srcPath).get(srcPath, snapshot, pluginDescription); } @Override - public String getPluginName(File srcFile) { - return providerOf(srcFile).getPluginName(srcFile); + public String getPluginName(Path srcPath) { + return providerOf(srcPath).getPluginName(srcPath); } @Override - public boolean handles(File srcFile) { - List<ServerPluginProvider> providers = - providersForHandlingPlugin(srcFile); + public boolean handles(Path srcPath) { + List<ServerPluginProvider> providers = providersForHandlingPlugin(srcPath); switch (providers.size()) { case 1: return true; case 0: return false; default: - throw new MultipleProvidersForPluginException(srcFile, providers); + throw new MultipleProvidersForPluginException(srcPath, providers); } } @@ -67,27 +66,27 @@ return "gerrit"; } - private ServerPluginProvider providerOf(File srcFile) { + private ServerPluginProvider providerOf(Path srcPath) { List<ServerPluginProvider> providers = - providersForHandlingPlugin(srcFile); + providersForHandlingPlugin(srcPath); switch (providers.size()) { case 1: return providers.get(0); case 0: throw new IllegalArgumentException( "No ServerPluginProvider found/loaded to handle plugin file " - + srcFile.getAbsolutePath()); + + srcPath.toAbsolutePath()); default: - throw new MultipleProvidersForPluginException(srcFile, providers); + throw new MultipleProvidersForPluginException(srcPath, providers); } } private List<ServerPluginProvider> providersForHandlingPlugin( - final File srcFile) { + final Path srcPath) { List<ServerPluginProvider> providers = new ArrayList<>(); for (ServerPluginProvider serverPluginProvider : serverPluginProviders) { - boolean handles = serverPluginProvider.handles(srcFile); - log.debug("File {} handled by {} ? => {}", srcFile, + boolean handles = serverPluginProvider.handles(srcPath); + log.debug("File {} handled by {} ? => {}", srcPath, serverPluginProvider.getProviderPluginName(), handles); if (handles) { providers.add(serverPluginProvider);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java index 98531ce..7168b1b2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
@@ -14,9 +14,9 @@ package com.google.gerrit.server.project; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.reviewdb.client.Branch; -import com.google.gerrit.server.project.ListBranches.BranchInfo; import com.google.inject.TypeLiteral; public class BranchResource extends ProjectResource {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java index e96d3a5..41d4920 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
@@ -14,6 +14,7 @@ package com.google.gerrit.server.project; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.BadRequestException; @@ -21,7 +22,7 @@ import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestView; -import com.google.gerrit.server.project.ListBranches.BranchInfo; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -56,9 +57,8 @@ public BranchResource parse(ProjectResource parent, IdString id) throws ResourceNotFoundException, IOException, BadRequestException { String branchName = id.get(); - if (!branchName.startsWith(Constants.R_REFS) - && !branchName.equals(Constants.HEAD)) { - branchName = Constants.R_HEADS + branchName; + if (!branchName.equals(Constants.HEAD)) { + branchName = RefNames.fullName(branchName); } List<BranchInfo> branches = list.get().apply(parent); for (BranchInfo b : branches) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java index f68acdd..4257825 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectResource.java
@@ -15,28 +15,34 @@ package com.google.gerrit.server.project; import com.google.common.collect.Iterables; +import com.google.gerrit.extensions.restapi.RestResource; import com.google.gerrit.extensions.restapi.RestView; import com.google.inject.TypeLiteral; -public class ChildProjectResource extends ProjectResource { +public class ChildProjectResource implements RestResource { public static final TypeLiteral<RestView<ChildProjectResource>> CHILD_PROJECT_KIND = new TypeLiteral<RestView<ChildProjectResource>>() {}; + private final ProjectResource parent; private final ProjectControl child; - public ChildProjectResource(ProjectResource project, ProjectControl child) { - super(project); + public ChildProjectResource(ProjectResource parent, ProjectControl child) { + this.parent = parent; this.child = child; } + public ProjectResource getParent() { + return parent; + } + public ProjectControl getChild() { return child; } public boolean isDirectChild() { - ProjectState parent = + ProjectState firstParent = Iterables.getFirst(child.getProjectState().parents(), null); - return parent != null - && getNameKey().equals(parent.getProject().getNameKey()); + return firstParent != null + && parent.getNameKey().equals(firstParent.getProject().getNameKey()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java index ca98cf6..faba87a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
@@ -44,7 +44,7 @@ } @Override - public RestView<ProjectResource> list() throws ResourceNotFoundException, + public ListChildProjects list() throws ResourceNotFoundException, AuthException { return list.get(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java index ac40df0..cb94063 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
@@ -17,6 +17,7 @@ import com.google.common.collect.Iterables; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.errors.InvalidRevisionException; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.DefaultInput; @@ -30,7 +31,6 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.project.CreateBranch.Input; -import com.google.gerrit.server.project.ListBranches.BranchInfo; import com.google.gerrit.server.util.MagicBranch; import com.google.inject.Inject; import com.google.inject.Provider; @@ -48,6 +48,7 @@ import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,9 +106,7 @@ while (ref.startsWith("/")) { ref = ref.substring(1); } - if (!ref.startsWith(Constants.R_REFS)) { - ref = Constants.R_HEADS + ref; - } + ref = RefNames.fullName(ref); if (!Repository.isValidRefName(ref)) { throw new BadRequestException("invalid branch name \"" + ref + "\""); } @@ -151,7 +150,7 @@ case FAST_FORWARD: case NEW: case NO_CHANGE: - referenceUpdated.fire(name.getParentKey(), u); + referenceUpdated.fire(name.getParentKey(), u, ReceiveCommand.Type.CREATE); hooks.doRefUpdatedHook(name, u, identifiedUser.get().getAccount()); break; case LOCK_FAILURE: @@ -174,7 +173,11 @@ } } - return new BranchInfo(ref, revid.getName(), refControl.canDelete()); + BranchInfo info = new BranchInfo(); + info.ref = ref; + info.revision = revid.getName(); + info.canDelete = refControl.canDelete() ? true : null; + return info; } catch (IOException err) { log.error("Cannot create branch \"" + name + "\"", err); throw err;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java index b0ac201..0abffde 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -17,13 +17,19 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.Lists; +import com.google.gerrit.common.ProjectUtil; +import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.errors.ProjectCreationFailedException; +import com.google.gerrit.common.data.GroupDescription; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.client.InheritableBoolean; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ProjectInfo; +import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.ResourceConflictException; @@ -34,8 +40,17 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.account.GroupBackend; +import com.google.gerrit.server.config.ProjectOwnerGroupsProvider; +import com.google.gerrit.server.config.RepositoryConfig; +import com.google.gerrit.server.extensions.events.GitReferenceUpdated; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.git.RepositoryCaseMismatchException; import com.google.gerrit.server.group.GroupsCollection; import com.google.gerrit.server.validators.ProjectCreationValidationListener; import com.google.gerrit.server.validators.ValidationException; @@ -44,8 +59,22 @@ import com.google.inject.assistedinject.Assisted; import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; @RequiresCapability(GlobalCapability.CREATE_PROJECT) @@ -54,40 +83,68 @@ CreateProject create(String name); } - private final PerformCreateProject.Factory createProjectFactory; + private static final Logger log = LoggerFactory + .getLogger(CreateProject.class); + private final Provider<ProjectsCollection> projectsCollection; private final Provider<GroupsCollection> groupsCollection; private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners; private final ProjectJson json; private final ProjectControl.GenericFactory projectControlFactory; + private final GitRepositoryManager repoManager; + private final DynamicSet<NewProjectCreatedListener> createdListener; + private final ProjectCache projectCache; + private final GroupBackend groupBackend; + private final ProjectOwnerGroupsProvider.Factory projectOwnerGroups; + private final MetaDataUpdate.User metaDataUpdateFactory; + private final GitReferenceUpdated referenceUpdated; + private final RepositoryConfig repositoryCfg; + private final PersonIdent serverIdent; private final Provider<CurrentUser> currentUser; private final Provider<PutConfig> putConfig; private final String name; @Inject - CreateProject(PerformCreateProject.Factory performCreateProjectFactory, - Provider<ProjectsCollection> projectsCollection, + CreateProject(Provider<ProjectsCollection> projectsCollection, Provider<GroupsCollection> groupsCollection, ProjectJson json, DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners, ProjectControl.GenericFactory projectControlFactory, - Provider<CurrentUser> currentUser, Provider<PutConfig> putConfig, + GitRepositoryManager repoManager, + DynamicSet<NewProjectCreatedListener> createdListener, + ProjectCache projectCache, + GroupBackend groupBackend, + ProjectOwnerGroupsProvider.Factory projectOwnerGroups, + MetaDataUpdate.User metaDataUpdateFactory, + GitReferenceUpdated referenceUpdated, + RepositoryConfig repositoryCfg, + @GerritPersonIdent PersonIdent serverIdent, + Provider<CurrentUser> currentUser, + Provider<PutConfig> putConfig, @Assisted String name) { - this.createProjectFactory = performCreateProjectFactory; this.projectsCollection = projectsCollection; this.groupsCollection = groupsCollection; this.projectCreationValidationListeners = projectCreationValidationListeners; this.json = json; this.projectControlFactory = projectControlFactory; + this.repoManager = repoManager; + this.createdListener = createdListener; + this.projectCache = projectCache; + this.groupBackend = groupBackend; + this.projectOwnerGroups = projectOwnerGroups; + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.referenceUpdated = referenceUpdated; + this.repositoryCfg = repositoryCfg; + this.serverIdent = serverIdent; this.currentUser = currentUser; this.putConfig = putConfig; this.name = name; } @Override - public Response<ProjectInfo> apply(TopLevelResource resource, ProjectInput input) - throws BadRequestException, UnprocessableEntityException, - ResourceConflictException, ProjectCreationFailedException, - ResourceNotFoundException, IOException { + public Response<ProjectInfo> apply(TopLevelResource resource, + ProjectInput input) throws BadRequestException, + UnprocessableEntityException, ResourceConflictException, + ResourceNotFoundException, IOException, ConfigInvalidException { if (input == null) { input = new ProjectInput(); } @@ -95,8 +152,9 @@ throw new BadRequestException("name must match URL"); } - final CreateProjectArgs args = new CreateProjectArgs(); - args.setProjectName(name); + CreateProjectArgs args = new CreateProjectArgs(); + args.setProjectName(ProjectUtil.stripGitSuffix(name)); + if (!Strings.isNullOrEmpty(input.parent)) { args.newParent = projectsCollection.get().parse(input.parent).getControl(); } @@ -104,14 +162,16 @@ args.permissionsOnly = input.permissionsOnly; args.projectDescription = Strings.emptyToNull(input.description); args.submitType = input.submitType; - args.branch = input.branches; - if (input.owners != null) { - List<AccountGroup.UUID> ownerIds = - Lists.newArrayListWithCapacity(input.owners.size()); + args.branch = normalizeBranchNames(input.branches); + if (input.owners == null || input.owners.isEmpty()) { + args.ownerIds = + new ArrayList<>(projectOwnerGroups.create(args.getProject()).get()); + } else { + args.ownerIds = + Lists.newArrayListWithCapacity(input.owners.size()); for (String owner : input.owners) { - ownerIds.add(groupsCollection.get().parse(owner).getGroupUUID()); + args.ownerIds.add(groupsCollection.get().parse(owner).getGroupUUID()); } - args.ownerIds = ownerIds; } args.contributorAgreements = MoreObjects.firstNonNull(input.useContributorAgreements, @@ -144,7 +204,7 @@ } } - Project p = createProjectFactory.create(args).createProject(); + Project p = createProject(args); if (input.pluginConfigValues != null) { try { @@ -160,4 +220,181 @@ return Response.created(json.format(p)); } + + public Project createProject(CreateProjectArgs args) + throws BadRequestException, ResourceConflictException, IOException, + ConfigInvalidException { + final Project.NameKey nameKey = args.getProject(); + try { + final String head = + args.permissionsOnly ? RefNames.REFS_CONFIG + : args.branch.get(0); + Repository repo = repoManager.createRepository(nameKey); + try { + NewProjectCreatedListener.Event event = new NewProjectCreatedListener.Event() { + @Override + public String getProjectName() { + return nameKey.get(); + } + + @Override + public String getHeadName() { + return head; + } + }; + for (NewProjectCreatedListener l : createdListener) { + try { + l.onNewProjectCreated(event); + } catch (RuntimeException e) { + log.warn("Failure in NewProjectCreatedListener", e); + } + } + + RefUpdate u = repo.updateRef(Constants.HEAD); + u.disableRefLog(); + u.link(head); + + createProjectConfig(args); + + if (!args.permissionsOnly + && args.createEmptyCommit) { + createEmptyCommits(repo, nameKey, args.branch); + } + + return projectCache.get(nameKey).getProject(); + } finally { + repo.close(); + } + } catch (RepositoryCaseMismatchException e) { + throw new ResourceConflictException("Cannot create " + nameKey.get() + + " because the name is already occupied by another project." + + " The other project has the same name, only spelled in a" + + " different case."); + } catch (RepositoryNotFoundException badName) { + throw new BadRequestException("invalid project name: " + nameKey); + } catch (IllegalStateException err) { + try { + Repository repo = repoManager.openRepository(nameKey); + try { + if (repo.getObjectDatabase().exists()) { + throw new ResourceConflictException("project \"" + nameKey + "\" exists"); + } + throw err; + } finally { + repo.close(); + } + } catch (IOException ioErr) { + String msg = "Cannot create " + nameKey; + log.error(msg, err); + throw ioErr; + } + } catch (ConfigInvalidException e) { + String msg = "Cannot create " + nameKey; + log.error(msg, e); + throw e; + } + } + + private void createProjectConfig(CreateProjectArgs args) throws IOException, ConfigInvalidException { + MetaDataUpdate md = + metaDataUpdateFactory.create(args.getProject()); + try { + ProjectConfig config = ProjectConfig.read(md); + config.load(md); + + Project newProject = config.getProject(); + newProject.setDescription(args.projectDescription); + newProject.setSubmitType(MoreObjects.firstNonNull(args.submitType, + repositoryCfg.getDefaultSubmitType(args.getProject()))); + newProject + .setUseContributorAgreements(args.contributorAgreements); + newProject.setUseSignedOffBy(args.signedOffBy); + newProject.setUseContentMerge(args.contentMerge); + newProject.setCreateNewChangeForAllNotInTarget(args.newChangeForAllNotInTarget); + newProject.setRequireChangeID(args.changeIdRequired); + newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit); + if (args.newParent != null) { + newProject.setParentName(args.newParent.getProject() + .getNameKey()); + } + + if (!args.ownerIds.isEmpty()) { + AccessSection all = + config.getAccessSection(AccessSection.ALL, true); + for (AccountGroup.UUID ownerId : args.ownerIds) { + GroupDescription.Basic g = groupBackend.get(ownerId); + if (g != null) { + GroupReference group = config.resolve(GroupReference.forGroup(g)); + all.getPermission(Permission.OWNER, true).add( + new PermissionRule(group)); + } + } + } + + md.setMessage("Created project\n"); + config.commit(md); + } finally { + md.close(); + } + projectCache.onCreateProject(args.getProject()); + repoManager.setProjectDescription(args.getProject(), + args.projectDescription); + } + + private List<String> normalizeBranchNames(List<String> branches) + throws BadRequestException { + if (branches == null || branches.isEmpty()) { + return Collections.singletonList(Constants.R_HEADS + Constants.MASTER); + } + + List<String> normalizedBranches = new ArrayList<>(); + for (String branch : branches) { + while (branch.startsWith("/")) { + branch = branch.substring(1); + } + branch = RefNames.fullName(branch); + if (!Repository.isValidRefName(branch)) { + throw new BadRequestException(String.format( + "Branch \"%s\" is not a valid name.", branch)); + } + if (!normalizedBranches.contains(branch)) { + normalizedBranches.add(branch); + } + } + return normalizedBranches; + } + + private void createEmptyCommits(Repository repo, Project.NameKey project, + List<String> refs) throws IOException { + try (ObjectInserter oi = repo.newObjectInserter()) { + CommitBuilder cb = new CommitBuilder(); + cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {})); + cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent()); + cb.setCommitter(serverIdent); + cb.setMessage("Initial empty repository\n"); + + ObjectId id = oi.insert(cb); + oi.flush(); + + for (String ref : refs) { + RefUpdate ru = repo.updateRef(ref); + ru.setNewObjectId(id); + Result result = ru.update(); + switch (result) { + case NEW: + referenceUpdated.fire(project, ru, ReceiveCommand.Type.CREATE); + break; + default: { + throw new IOException(String.format( + "Failed to create ref \"%s\": %s", ref, result.name())); + } + } + } + } catch (IOException e) { + log.error( + "Cannot create empty commit for " + + project.get(), e); + throw e; + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java index 4aba333..202dc0e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
@@ -35,6 +35,7 @@ import org.eclipse.jgit.errors.LockFailedException; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceiveCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +47,7 @@ private static final int MAX_LOCK_FAILURE_CALLS = 10; private static final long SLEEP_ON_LOCK_FAILURE_MS = 15; - static class Input { + public static class Input { } private final Provider<IdentifiedUser> identifiedUser; @@ -113,7 +114,7 @@ case NO_CHANGE: case FAST_FORWARD: case FORCED: - referenceUpdated.fire(rsrc.getNameKey(), u); + referenceUpdated.fire(rsrc.getNameKey(), u, ReceiveCommand.Type.DELETE); hooks.doRefUpdatedHook(rsrc.getBranchKey(), u, identifiedUser.get().getAccount()); ResultSet<SubmoduleSubscription> submoduleSubscriptions = dbProvider.get().submoduleSubscriptions().bySuperProject(rsrc.getBranchKey());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java index d6e93f0..fc34917 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java
@@ -165,8 +165,7 @@ private void postDeletion(ProjectResource project, ReceiveCommand cmd) throws OrmException { - referenceUpdated.fire(project.getNameKey(), cmd.getRefName(), - cmd.getOldId(), cmd.getNewId()); + referenceUpdated.fire(project.getNameKey(), cmd); Branch.NameKey branchKey = new Branch.NameKey(project.getNameKey(), cmd.getRefName()); hooks.doRefUpdatedHook(branchKey, cmd.getOldId(), cmd.getNewId(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java index b525399..7702b7d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteDashboard.java
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.server.project.DashboardsCollection.DashboardInfo; -import com.google.gerrit.server.project.DeleteDashboard.Input; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -30,11 +29,7 @@ import java.io.IOException; @Singleton -class DeleteDashboard implements RestModifyView<DashboardResource, Input> { - static class Input { - String commitMessage; - } - +class DeleteDashboard implements RestModifyView<DashboardResource, SetDashboard.Input> { private final Provider<SetDefaultDashboard> defaultSetter; @Inject @@ -43,7 +38,7 @@ } @Override - public Response<DashboardInfo> apply(DashboardResource resource, Input input) + public Response<DashboardInfo> apply(DashboardResource resource, SetDashboard.Input input) throws AuthException, BadRequestException, ResourceConflictException, ResourceNotFoundException, MethodNotAllowedException, IOException { if (resource.isProjectDefault()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java index fe1086b..a5a96b1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GarbageCollect.java
@@ -41,6 +41,7 @@ UiAction<ProjectResource> { public static class Input { public boolean showProgress; + public boolean aggressive; } private final boolean canGC; @@ -68,8 +69,10 @@ } }; try { - GarbageCollectionResult result = garbageCollectionFactory.create().run( - Collections.singletonList(rsrc.getNameKey()), input.showProgress ? writer : null); + GarbageCollectionResult result = + garbageCollectionFactory.create().run( + Collections.singletonList(rsrc.getNameKey()), input.aggressive, + input.showProgress ? writer : null); String msg = "Garbage collection completed successfully."; if (result.hasErrors()) { for (GarbageCollectionResult.Error e : result.getErrors()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetBranch.java index 59b15d8..78878a7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetBranch.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetBranch.java
@@ -14,8 +14,8 @@ package com.google.gerrit.server.project; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.restapi.RestReadView; -import com.google.gerrit.server.project.ListBranches.BranchInfo; import com.google.inject.Singleton; @Singleton
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetChildProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetChildProject.java index 815653f..7737d8c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetChildProject.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetChildProject.java
@@ -23,9 +23,12 @@ public class GetChildProject implements RestReadView<ChildProjectResource> { @Option(name = "--recursive", usage = "to list child projects recursively") - private boolean recursive; + public void setRecursive(boolean recursive) { + this.recursive = recursive; + } private final ProjectJson json; + private boolean recursive; @Inject GetChildProject(ProjectJson json) { @@ -38,6 +41,6 @@ if (recursive || rsrc.isDirectChild()) { return json.format(rsrc.getChild().getProject()); } - throw new ResourceNotFoundException(rsrc.getName()); + throw new ResourceNotFoundException(rsrc.getChild().getProject().getName()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java index 5241c69..bace0a8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDescription.java
@@ -20,7 +20,7 @@ import com.google.inject.Singleton; @Singleton -class GetDescription implements RestReadView<ProjectResource> { +public class GetDescription implements RestReadView<ProjectResource> { @Override public String apply(ProjectResource resource) { Project project = resource.getControl().getProject();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java index a8eda97..8195c2a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
@@ -15,10 +15,11 @@ package com.google.gerrit.server.project; import com.google.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.ComparisonChain; import com.google.common.collect.FluentIterable; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.gerrit.extensions.api.projects.BranchInfo; import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.common.WebLinkInfo; import com.google.gerrit.extensions.registration.DynamicMap; @@ -45,11 +46,11 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -59,15 +60,28 @@ private final WebLinks webLinks; @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of branches to list") - private int limit; + public void setLimit(int limit) { + this.limit = limit; + } @Option(name = "--start", aliases = {"-s"}, metaVar = "CNT", usage = "number of branches to skip") - private int start; + public void setStart(int start) { + this.start = start; + } @Option(name = "--match", aliases = {"-m"}, metaVar = "MATCH", usage = "match branches substring") - private String matchSubstring; + public void setMatchSubstring(String matchSubstring) { + this.matchSubstring = matchSubstring; + } @Option(name = "--regex", aliases = {"-r"}, metaVar = "REGEX", usage = "match branches regex") + public void setMatchRegex(String matchRegex) { + this.matchRegex = matchRegex; + } + + private int limit; + private int start; + private String matchSubstring; private String matchRegex; @Inject @@ -82,171 +96,163 @@ @Override public List<BranchInfo> apply(ProjectResource rsrc) throws ResourceNotFoundException, IOException, BadRequestException { - List<BranchInfo> branches = Lists.newArrayList(); + FluentIterable<BranchInfo> branches = allBranches(rsrc); + branches = filterBranches(branches); + if (start > 0) { + branches = branches.skip(start); + } + if (limit > 0) { + branches = branches.limit(limit); + } + return branches.toList(); + } - BranchInfo headBranch = null; - BranchInfo configBranch = null; - final Set<String> targets = Sets.newHashSet(); - - final Repository db; - try { - db = repoManager.openRepository(rsrc.getNameKey()); + private FluentIterable<BranchInfo> allBranches(ProjectResource rsrc) + throws IOException, ResourceNotFoundException { + List<Ref> refs; + try (Repository db = repoManager.openRepository(rsrc.getNameKey())) { + Collection<Ref> heads = + db.getRefDatabase().getRefs(Constants.R_HEADS).values(); + refs = new ArrayList<>(heads.size() + 3); + refs.addAll(heads); + addRef(db, refs, Constants.HEAD); + addRef(db, refs, RefNames.REFS_CONFIG); + addRef(db, refs, RefNames.REFS_USERS_DEFAULT); } catch (RepositoryNotFoundException noGitRepository) { throw new ResourceNotFoundException(); } - try { - List<Ref> refs = - new ArrayList<>(db.getRefDatabase().getRefs(Constants.R_HEADS) - .values()); - - try { - Ref head = db.getRef(Constants.HEAD); - if (head != null) { - refs.add(head); - } - } catch (IOException e) { - // Ignore the failure reading HEAD. - } - try { - Ref config = db.getRef(RefNames.REFS_CONFIG); - if (config != null) { - refs.add(config); - } - } catch (IOException e) { - // Ignore the failure reading refs/meta/config. - } - - for (Ref ref : refs) { - if (ref.isSymbolic()) { - targets.add(ref.getTarget().getName()); - } + Set<String> targets = Sets.newHashSetWithExpectedSize(1); + for (Ref ref : refs) { + if (ref.isSymbolic()) { + targets.add(ref.getTarget().getName()); } + } - for (Ref ref : refs) { - if (ref.isSymbolic()) { - // A symbolic reference to another branch, instead of - // showing the resolved value, show the name it references. - // - String target = ref.getTarget().getName(); - RefControl targetRefControl = rsrc.getControl().controlForRef(target); - if (!targetRefControl.isVisible()) { - continue; - } - if (target.startsWith(Constants.R_HEADS)) { - target = target.substring(Constants.R_HEADS.length()); - } - - BranchInfo b = new BranchInfo(ref.getName(), target, false); - - if (Constants.HEAD.equals(ref.getName())) { - headBranch = b; - } else { - b.setCanDelete(targetRefControl.canDelete()); - branches.add(b); - } + List<BranchInfo> branches = new ArrayList<>(refs.size()); + for (Ref ref : refs) { + if (ref.isSymbolic()) { + // A symbolic reference to another branch, instead of + // showing the resolved value, show the name it references. + // + String target = ref.getTarget().getName(); + RefControl targetRefControl = rsrc.getControl().controlForRef(target); + if (!targetRefControl.isVisible()) { continue; } - - final RefControl refControl = rsrc.getControl().controlForRef(ref.getName()); - if (refControl.isVisible()) { - if (RefNames.REFS_CONFIG.equals(ref.getName())) { - configBranch = createBranchInfo(ref, refControl, targets); - } else { - branches.add(createBranchInfo(ref, refControl, targets)); - } + if (target.startsWith(Constants.R_HEADS)) { + target = target.substring(Constants.R_HEADS.length()); } - } - } finally { - db.close(); - } - Collections.sort(branches, new Comparator<BranchInfo>() { - @Override - public int compare(final BranchInfo a, final BranchInfo b) { - return a.ref.compareTo(b.ref); - } - }); - if (configBranch != null) { - branches.add(0, configBranch); - } - if (headBranch != null) { - branches.add(0, headBranch); - } - List<BranchInfo> filteredBranches; - if ((matchSubstring != null && !matchSubstring.isEmpty()) - || (matchRegex != null && !matchRegex.isEmpty())) { - filteredBranches = filterBranches(branches); - } else { - filteredBranches = branches; - } - if (!filteredBranches.isEmpty()) { - int end = filteredBranches.size(); - if (limit > 0 && start + limit < end) { - end = start + limit; + BranchInfo b = new BranchInfo(); + b.ref = ref.getName(); + b.revision = target; + branches.add(b); + + if (!Constants.HEAD.equals(ref.getName())) { + b.canDelete = targetRefControl.canDelete() ? true : null; + } + continue; } - if (start <= end) { - filteredBranches = filteredBranches.subList(start, end); - } else { - filteredBranches = Collections.emptyList(); + + RefControl refControl = rsrc.getControl().controlForRef(ref.getName()); + if (refControl.isVisible()) { + branches.add(createBranchInfo(ref, refControl, targets)); } } - return filteredBranches; + Collections.sort(branches, new BranchComparator()); + return FluentIterable.from(branches); } - private List<BranchInfo> filterBranches(List<BranchInfo> branches) - throws BadRequestException { - if (matchSubstring != null) { - return Lists.newArrayList(Iterables.filter(branches, - new Predicate<BranchInfo>() { - @Override - public boolean apply(BranchInfo in) { - if (!in.ref.startsWith(Constants.R_HEADS)){ - return in.ref.toLowerCase(Locale.US).contains( - matchSubstring.toLowerCase(Locale.US)); - } else { - return in.ref.substring(Constants.R_HEADS.length()) - .toLowerCase(Locale.US) - .contains(matchSubstring.toLowerCase(Locale.US)); - } - } - })); - } else if (matchRegex != null) { - if (matchRegex.startsWith("^")) { - matchRegex = matchRegex.substring(1); - if (matchRegex.endsWith("$") && !matchRegex.endsWith("\\$")) { - matchRegex = matchRegex.substring(0, matchRegex.length() - 1); - } - } - if (matchRegex.equals(".*")) { - return branches; - } - try { - final RunAutomaton a = - new RunAutomaton(new RegExp(matchRegex).toAutomaton()); - return Lists.newArrayList(Iterables.filter( - branches, new Predicate<BranchInfo>() { - @Override - public boolean apply(BranchInfo in) { - if (!in.ref.startsWith(Constants.R_HEADS)){ - return a.run(in.ref); - } else { - return a.run(in.ref.substring(Constants.R_HEADS.length())); - } - } - })); - } catch (IllegalArgumentException e) { - throw new BadRequestException(e.getMessage()); - } + private static class BranchComparator implements Comparator<BranchInfo> { + @Override + public int compare(BranchInfo a, BranchInfo b) { + return ComparisonChain.start() + .compareTrueFirst(isHead(a), isHead(b)) + .compareTrueFirst(isConfig(a), isConfig(b)) + .compare(a.ref, b.ref) + .result(); + } + + private static boolean isHead(BranchInfo i) { + return Constants.HEAD.equals(i.ref); + } + + private static boolean isConfig(BranchInfo i) { + return RefNames.REFS_CONFIG.equals(i.ref); + } + } + + private static void addRef(Repository db, List<Ref> refs, String name) + throws IOException { + Ref ref = db.getRef(name); + if (ref != null) { + refs.add(ref); + } + } + + private FluentIterable<BranchInfo> filterBranches( + FluentIterable<BranchInfo> branches) throws BadRequestException { + if (!Strings.isNullOrEmpty(matchSubstring)) { + branches = branches.filter(new SubstringPredicate(matchSubstring)); + } else if (!Strings.isNullOrEmpty(matchRegex)) { + branches = branches.filter(new RegexPredicate(matchRegex)); } return branches; } + private static class SubstringPredicate implements Predicate<BranchInfo> { + private final String substring; + + private SubstringPredicate(String substring) { + this.substring = substring.toLowerCase(Locale.US); + } + + @Override + public boolean apply(BranchInfo in) { + String ref = in.ref; + if (ref.startsWith(Constants.R_HEADS)) { + ref = ref.substring(Constants.R_HEADS.length()); + } + ref = ref.toLowerCase(Locale.US); + return ref.contains(substring); + } + } + + private static class RegexPredicate implements Predicate<BranchInfo> { + private final RunAutomaton a; + + private RegexPredicate(String regex) throws BadRequestException { + if (regex.startsWith("^")) { + regex = regex.substring(1); + if (regex.endsWith("$") && !regex.endsWith("\\$")) { + regex = regex.substring(0, regex.length() - 1); + } + } + try { + a = new RunAutomaton(new RegExp(regex).toAutomaton()); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + } + + @Override + public boolean apply(BranchInfo in) { + if (!in.ref.startsWith(Constants.R_HEADS)){ + return a.run(in.ref); + } else { + return a.run(in.ref.substring(Constants.R_HEADS.length())); + } + } + } + private BranchInfo createBranchInfo(Ref ref, RefControl refControl, Set<String> targets) { - BranchInfo info = new BranchInfo(ref.getName(), - ref.getObjectId() != null ? ref.getObjectId().name() : null, - !targets.contains(ref.getName()) && refControl.canDelete()); + BranchInfo info = new BranchInfo(); + info.ref = ref.getName(); + info.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null; + info.canDelete = !targets.contains(ref.getName()) && refControl.canDelete() + ? true : null; for (UiAction.Description d : UiActions.from( branchViews, new BranchResource(refControl.getProjectControl(), info), @@ -262,22 +268,4 @@ info.webLinks = links.isEmpty() ? null : links.toList(); return info; } - - public static class BranchInfo { - public String ref; - public String revision; - public Boolean canDelete; - public Map<String, ActionInfo> actions; - public List<WebLinkInfo> webLinks; - - public BranchInfo(String ref, String revision, boolean canDelete) { - this.ref = ref; - this.revision = revision; - this.canDelete = canDelete; - } - - void setCanDelete(boolean canDelete) { - this.canDelete = canDelete ? true : null; - } - } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java index a9851e4..2d8757e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -66,6 +66,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; @@ -236,12 +237,12 @@ return apply(); } - public Map<String, ProjectInfo> apply() throws BadRequestException { + public SortedMap<String, ProjectInfo> apply() throws BadRequestException { format = OutputFormat.JSON; return display(null); } - public Map<String, ProjectInfo> display(OutputStream displayOutputStream) + public SortedMap<String, ProjectInfo> display(OutputStream displayOutputStream) throws BadRequestException { PrintWriter stdout = null; if (displayOutputStream != null) { @@ -255,7 +256,7 @@ int foundIndex = 0; int found = 0; - Map<String, ProjectInfo> output = Maps.newTreeMap(); + TreeMap<String, ProjectInfo> output = Maps.newTreeMap(); Map<String, String> hiddenNames = Maps.newHashMap(); Set<String> rejected = new HashSet<>();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java deleted file mode 100644 index 689920b..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java +++ /dev/null
@@ -1,304 +0,0 @@ -// Copyright (C) 2011 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License - -package com.google.gerrit.server.project; - -import com.google.common.base.MoreObjects; -import com.google.gerrit.common.ProjectUtil; -import com.google.gerrit.common.data.AccessSection; -import com.google.gerrit.common.data.GroupDescription; -import com.google.gerrit.common.data.GroupReference; -import com.google.gerrit.common.data.Permission; -import com.google.gerrit.common.data.PermissionRule; -import com.google.gerrit.common.errors.ProjectCreationFailedException; -import com.google.gerrit.extensions.client.SubmitType; -import com.google.gerrit.extensions.events.NewProjectCreatedListener; -import com.google.gerrit.extensions.registration.DynamicSet; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RefNames; -import com.google.gerrit.server.GerritPersonIdent; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.account.GroupBackend; -import com.google.gerrit.server.config.GerritServerConfig; -import com.google.gerrit.server.config.ProjectOwnerGroups; -import com.google.gerrit.server.extensions.events.GitReferenceUpdated; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.MetaDataUpdate; -import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.git.RepositoryCaseMismatchException; -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; - -import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.RefUpdate.Result; -import org.eclipse.jgit.lib.Repository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - - -/** Common class that holds the code to create projects */ -public class PerformCreateProject { - private static final Logger log = LoggerFactory - .getLogger(PerformCreateProject.class); - - public interface Factory { - PerformCreateProject create(CreateProjectArgs createProjectArgs); - } - - private final Config cfg; - private final Set<AccountGroup.UUID> projectOwnerGroups; - private final IdentifiedUser currentUser; - private final GitRepositoryManager repoManager; - private final GitReferenceUpdated referenceUpdated; - private final DynamicSet<NewProjectCreatedListener> createdListener; - private final PersonIdent serverIdent; - private final CreateProjectArgs createProjectArgs; - private final ProjectCache projectCache; - private final GroupBackend groupBackend; - private final MetaDataUpdate.User metaDataUpdateFactory; - - @Inject - PerformCreateProject(@GerritServerConfig Config cfg, - @ProjectOwnerGroups Set<AccountGroup.UUID> pOwnerGroups, - IdentifiedUser identifiedUser, GitRepositoryManager gitRepoManager, - GitReferenceUpdated referenceUpdated, - DynamicSet<NewProjectCreatedListener> createdListener, - @GerritPersonIdent PersonIdent personIdent, GroupBackend groupBackend, - MetaDataUpdate.User metaDataUpdateFactory, - @Assisted CreateProjectArgs createPArgs, ProjectCache pCache) { - this.cfg = cfg; - this.projectOwnerGroups = pOwnerGroups; - this.currentUser = identifiedUser; - this.repoManager = gitRepoManager; - this.referenceUpdated = referenceUpdated; - this.createdListener = createdListener; - this.serverIdent = personIdent; - this.createProjectArgs = createPArgs; - this.projectCache = pCache; - this.groupBackend = groupBackend; - this.metaDataUpdateFactory = metaDataUpdateFactory; - } - - public Project createProject() throws ProjectCreationFailedException { - validateParameters(); - final Project.NameKey nameKey = createProjectArgs.getProject(); - try { - final String head = - createProjectArgs.permissionsOnly ? RefNames.REFS_CONFIG - : createProjectArgs.branch.get(0); - final Repository repo = repoManager.createRepository(nameKey); - try { - NewProjectCreatedListener.Event event = new NewProjectCreatedListener.Event() { - @Override - public String getProjectName() { - return nameKey.get(); - } - - @Override - public String getHeadName() { - return head; - } - }; - for (NewProjectCreatedListener l : createdListener) { - try { - l.onNewProjectCreated(event); - } catch (RuntimeException e) { - log.warn("Failure in NewProjectCreatedListener", e); - } - } - - final RefUpdate u = repo.updateRef(Constants.HEAD); - u.disableRefLog(); - u.link(head); - - createProjectConfig(); - - if (!createProjectArgs.permissionsOnly - && createProjectArgs.createEmptyCommit) { - createEmptyCommits(repo, nameKey, createProjectArgs.branch); - } - - return projectCache.get(nameKey).getProject(); - } finally { - repo.close(); - } - } catch (RepositoryCaseMismatchException e) { - throw new ProjectCreationFailedException("Cannot create " + nameKey.get() - + " because the name is already occupied by another project." - + " The other project has the same name, only spelled in a" - + " different case.", e); - } catch (RepositoryNotFoundException badName) { - throw new ProjectCreationFailedException("Cannot create " + nameKey, badName); - } catch (IllegalStateException err) { - try { - final Repository repo = repoManager.openRepository(nameKey); - try { - if (repo.getObjectDatabase().exists()) { - throw new ProjectCreationFailedException("project \"" + nameKey + "\" exists"); - } - throw err; - } finally { - repo.close(); - } - } catch (IOException ioErr) { - final String msg = "Cannot create " + nameKey; - log.error(msg, err); - throw new ProjectCreationFailedException(msg, ioErr); - } - } catch (Exception e) { - final String msg = "Cannot create " + nameKey; - log.error(msg, e); - throw new ProjectCreationFailedException(msg, e); - } - } - - private void createProjectConfig() throws IOException, ConfigInvalidException { - final MetaDataUpdate md = - metaDataUpdateFactory.create(createProjectArgs.getProject()); - try { - final ProjectConfig config = ProjectConfig.read(md); - config.load(md); - - Project newProject = config.getProject(); - newProject.setDescription(createProjectArgs.projectDescription); - newProject.setSubmitType(MoreObjects.firstNonNull(createProjectArgs.submitType, - cfg.getEnum("repository", "*", "defaultSubmitType", SubmitType.MERGE_IF_NECESSARY))); - newProject - .setUseContributorAgreements(createProjectArgs.contributorAgreements); - newProject.setUseSignedOffBy(createProjectArgs.signedOffBy); - newProject.setUseContentMerge(createProjectArgs.contentMerge); - newProject.setCreateNewChangeForAllNotInTarget(createProjectArgs.newChangeForAllNotInTarget); - newProject.setRequireChangeID(createProjectArgs.changeIdRequired); - newProject.setMaxObjectSizeLimit(createProjectArgs.maxObjectSizeLimit); - if (createProjectArgs.newParent != null) { - newProject.setParentName(createProjectArgs.newParent.getProject() - .getNameKey()); - } - - if (!createProjectArgs.ownerIds.isEmpty()) { - final AccessSection all = - config.getAccessSection(AccessSection.ALL, true); - for (AccountGroup.UUID ownerId : createProjectArgs.ownerIds) { - GroupDescription.Basic g = groupBackend.get(ownerId); - if (g != null) { - GroupReference group = config.resolve(GroupReference.forGroup(g)); - all.getPermission(Permission.OWNER, true).add( - new PermissionRule(group)); - } - } - } - - md.setMessage("Created project\n"); - config.commit(md); - } finally { - md.close(); - } - projectCache.onCreateProject(createProjectArgs.getProject()); - repoManager.setProjectDescription(createProjectArgs.getProject(), - createProjectArgs.projectDescription); - } - - private void validateParameters() throws ProjectCreationFailedException { - if (createProjectArgs.getProjectName() == null - || createProjectArgs.getProjectName().isEmpty()) { - throw new ProjectCreationFailedException("Project name is required"); - } - - String nameWithoutSuffix = ProjectUtil.stripGitSuffix(createProjectArgs.getProjectName()); - createProjectArgs.setProjectName(nameWithoutSuffix); - - if (!currentUser.getCapabilities().canCreateProject()) { - throw new ProjectCreationFailedException(String.format( - "%s does not have \"Create Project\" capability.", - currentUser.getUserName())); - } - - if (createProjectArgs.ownerIds == null - || createProjectArgs.ownerIds.isEmpty()) { - createProjectArgs.ownerIds = new ArrayList<>(projectOwnerGroups); - } - - List<String> transformedBranches = new ArrayList<>(); - if (createProjectArgs.branch == null || - createProjectArgs.branch.isEmpty()) { - createProjectArgs.branch = Collections.singletonList(Constants.MASTER); - } - for (String branch : createProjectArgs.branch) { - while (branch.startsWith("/")) { - branch = branch.substring(1); - } - if (!branch.startsWith(Constants.R_HEADS)) { - branch = Constants.R_HEADS + branch; - } - if (!Repository.isValidRefName(branch)) { - throw new ProjectCreationFailedException(String.format( - "Branch \"%s\" is not a valid name.", branch)); - } - if (!transformedBranches.contains(branch)) { - transformedBranches.add(branch); - } - } - createProjectArgs.branch = transformedBranches; - } - - private void createEmptyCommits(final Repository repo, - final Project.NameKey project, final List<String> refs) - throws IOException { - try (ObjectInserter oi = repo.newObjectInserter()) { - CommitBuilder cb = new CommitBuilder(); - cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {})); - cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent()); - cb.setCommitter(serverIdent); - cb.setMessage("Initial empty repository\n"); - - ObjectId id = oi.insert(cb); - oi.flush(); - - for (String ref : refs) { - RefUpdate ru = repo.updateRef(ref); - ru.setNewObjectId(id); - final Result result = ru.update(); - switch (result) { - case NEW: - referenceUpdated.fire(project, ru); - break; - default: { - throw new IOException(String.format( - "Failed to create ref \"%s\": %s", ref, result.name())); - } - } - } - } catch (IOException e) { - log.error( - "Cannot create empty commit for " - + createProjectArgs.getProjectName(), e); - throw e; - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 558b572..10b64a0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -22,7 +22,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.io.Files; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.LabelType; @@ -46,7 +45,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; import com.googlecode.prolog_cafe.lang.PrologMachineCopy; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -55,9 +54,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; -import java.io.InputStream; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -205,10 +205,10 @@ * read the provided input stream. * * @param name a name of the input stream. Could be any name. - * @param in InputStream to read prolog rules from + * @param in stream to read prolog rules from * @throws CompileException */ - public PrologEnvironment newPrologEnvironment(String name, InputStream in) + public PrologEnvironment newPrologEnvironment(String name, Reader in) throws CompileException { PrologMachineCopy pmc = rulesCache.loadMachine(name, in); return envFactory.create(pmc); @@ -488,25 +488,25 @@ private ThemeInfo loadTheme() { String name = getConfig().getProject().getName(); - File dir = new File(sitePaths.themes_dir, name); - if (!dir.exists()) { + Path dir = sitePaths.themes_dir.resolve(name); + if (!Files.exists(dir)) { return ThemeInfo.INHERIT; - } else if (!dir.isDirectory()) { + } else if (!Files.isDirectory(dir)) { log.warn("Bad theme for {}: not a directory", name); return ThemeInfo.INHERIT; } try { - return new ThemeInfo(readFile(new File(dir, SitePaths.CSS_FILENAME)), - readFile(new File(dir, SitePaths.HEADER_FILENAME)), - readFile(new File(dir, SitePaths.FOOTER_FILENAME))); + return new ThemeInfo(readFile(dir.resolve(SitePaths.CSS_FILENAME)), + readFile(dir.resolve(SitePaths.HEADER_FILENAME)), + readFile(dir.resolve(SitePaths.FOOTER_FILENAME))); } catch (IOException e) { log.error("Error reading theme for " + name, e); return ThemeInfo.INHERIT; } } - private String readFile(File f) throws IOException { - return f.exists() ? Files.toString(f, UTF_8) : null; + private String readFile(Path p) throws IOException { + return Files.exists(p) ? new String(Files.readAllBytes(p), UTF_8) : null; } private boolean getInheritableBoolean(Function<Project, InheritableBoolean> func) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java index 536bfa7..1d7c724 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
@@ -17,8 +17,8 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.gerrit.common.ChangeHooks; +import com.google.gerrit.extensions.api.projects.PutDescriptionInput; import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.Response; @@ -30,7 +30,6 @@ import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; -import com.google.gerrit.server.project.PutDescription.Input; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -42,13 +41,7 @@ import java.util.Objects; @Singleton -class PutDescription implements RestModifyView<ProjectResource, Input> { - static class Input { - @DefaultInput - String description; - String commitMessage; - } - +public class PutDescription implements RestModifyView<ProjectResource, PutDescriptionInput> { private final ProjectCache cache; private final MetaDataUpdate.Server updateFactory; private final GitRepositoryManager gitMgr; @@ -66,11 +59,11 @@ } @Override - public Response<String> apply(ProjectResource resource, Input input) - throws AuthException, ResourceConflictException, - ResourceNotFoundException, IOException { + public Response<String> apply(ProjectResource resource, + PutDescriptionInput input) throws AuthException, + ResourceConflictException, ResourceNotFoundException, IOException { if (input == null) { - input = new Input(); // Delete would set description to null. + input = new PutDescriptionInput(); // Delete would set description to null. } ProjectControl ctl = resource.getControl();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java index 71c3104..e507de7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -259,6 +259,7 @@ switch (getCurrentUser().getAccessPath()) { case REST_API: case JSON_RPC: + case UNKNOWN: owner = isOwner(); admin = getCurrentUser().getCapabilities().canAdministrateServer(); break; @@ -364,18 +365,13 @@ } switch (getCurrentUser().getAccessPath()) { - case REST_API: - case JSON_RPC: - case SSH_COMMAND: - return getCurrentUser().getCapabilities().canAdministrateServer() - || (isOwner() && !isForceBlocked(Permission.PUSH)) - || canPushWithForce(); - case GIT: return canPushWithForce(); default: - return false; + return getCurrentUser().getCapabilities().canAdministrateServer() + || (isOwner() && !isForceBlocked(Permission.PUSH)) + || canPushWithForce(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java index 9009aad..7b6b5c8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java
@@ -86,7 +86,7 @@ Collections.sort(sections, new MostSpecificComparator(ref)); - int srcIdx[]; + int[] srcIdx; if (isIdentityTransform(sections, srcMap)) { srcIdx = null; } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java index 77c221c..07c0162 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.project.SetHead.Input; @@ -43,9 +44,9 @@ public class SetHead implements RestModifyView<ProjectResource, Input> { private static final Logger log = LoggerFactory.getLogger(SetHead.class); - static class Input { + public static class Input { @DefaultInput - String ref; + public String ref; } private final GitRepositoryManager repoManager; @@ -71,10 +72,7 @@ if (input == null || Strings.isNullOrEmpty(input.ref)) { throw new BadRequestException("ref required"); } - String ref = input.ref; - if (!ref.startsWith(Constants.R_REFS)) { - ref = Constants.R_HEADS + ref; - } + String ref = RefNames.fullName(input.ref); Repository repo = null; try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java index 4df9831..e8e29c1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -16,7 +16,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; @@ -27,13 +26,13 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.rules.PrologEnvironment; -import com.google.gerrit.rules.ReductionLimitException; import com.google.gerrit.rules.StoredValues; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.query.change.ChangeData; import com.google.gwtorm.server.OrmException; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; +import com.googlecode.prolog_cafe.exceptions.ReductionLimitException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.ListTerm; import com.googlecode.prolog_cafe.lang.Prolog; @@ -45,7 +44,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; +import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -107,7 +106,7 @@ private boolean skipFilters; private String rule; private boolean logErrors = true; - private int reductionsConsumed; + private long reductionsConsumed; private Term submitRule; @@ -185,7 +184,7 @@ } /** @return Prolog reductions consumed during evaluation. */ - public int getReductionsConsumed() { + public long getReductionsConsumed() { return reductionsConsumed; } @@ -270,7 +269,7 @@ SubmitRecord rec = new SubmitRecord(); out.add(rec); - if (!submitRecord.isStructure() || 1 != submitRecord.arity()) { + if (!(submitRecord instanceof StructureTerm) || 1 != submitRecord.arity()) { return invalidResult(submitRule, submitRecord); } @@ -289,14 +288,16 @@ // submitRecord = submitRecord.arg(0); - if (!submitRecord.isStructure()) { + if (!(submitRecord instanceof StructureTerm)) { return invalidResult(submitRule, submitRecord); } rec.labels = new ArrayList<>(submitRecord.arity()); for (Term state : ((StructureTerm) submitRecord).args()) { - if (!state.isStructure() || 2 != state.arity() || !"label".equals(state.name())) { + if (!(state instanceof StructureTerm) + || 2 != state.arity() + || !"label".equals(state.name())) { return invalidResult(submitRule, submitRecord); } @@ -414,7 +415,7 @@ } Term typeTerm = results.get(0); - if (!typeTerm.isSymbol()) { + if (!(typeTerm instanceof SymbolTerm)) { return typeError("Submit rule '" + getSubmitRule() + "' for change " + cd.getId() + " of " + getProjectName() + " did not return a symbol."); @@ -485,9 +486,9 @@ resultsTerm, env, filterRuleLocatorName, filterRuleWrapperName); } List<Term> r; - if (resultsTerm.isList()) { + if (resultsTerm instanceof ListTerm) { r = Lists.newArrayList(); - for (Term t = resultsTerm; t.isList();) { + for (Term t = resultsTerm; t instanceof ListTerm;) { ListTerm l = (ListTerm) t; r.add(l.car().dereference()); t = l.cdr().dereference(); @@ -510,12 +511,20 @@ if (rule == null) { env = projectState.newPrologEnvironment(); } else { - env = projectState.newPrologEnvironment( - "stdin", new ByteArrayInputStream(rule.getBytes(UTF_8))); + env = projectState.newPrologEnvironment("stdin", new StringReader(rule)); } } catch (CompileException err) { - throw new RuleEvalException("Cannot consult rules.pl for " - + getProjectName(), err); + String msg; + if (rule == null && control.getProjectControl().isOwner()) { + msg = String.format( + "Cannot load rules.pl for %s: %s", + getProjectName(), err.getMessage()); + } else if (rule != null) { + msg = err.getMessage(); + } else { + msg = String.format("Cannot load rules.pl for %s", getProjectName()); + } + throw new RuleEvalException(msg, err); } env.set(StoredValues.REVIEW_DB, cd.db()); env.set(StoredValues.CHANGE_DATA, cd); @@ -578,7 +587,7 @@ private void appliedBy(SubmitRecord.Label label, Term status) throws UserTermExpected { - if (status.isStructure() && status.arity() == 1) { + if (status instanceof StructureTerm && status.arity() == 1) { Term who = status.arg(0); if (isUser(who)) { label.appliedBy = new Account.Id(((IntegerTerm) who.arg(0)).intValue()); @@ -589,10 +598,10 @@ } private static boolean isUser(Term who) { - return who.isStructure() + return who instanceof StructureTerm && who.arity() == 1 && who.name().equals("user") - && who.arg(0).isInteger(); + && who.arg(0) instanceof IntegerTerm; } public Term getSubmitRule() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/IntPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/IntPredicate.java index e3750fa..d336bb5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/IntPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/IntPredicate.java
@@ -39,8 +39,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } if (getClass() == other.getClass()) { final IntPredicate<?> p = (IntPredicate<?>) other; return getOperator().equals(p.getOperator())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java index 6a9a877..248fb9c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
@@ -74,8 +74,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } return getClass() == other.getClass() && getChildren().equals(((Predicate<?>) other).getChildren()); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java index 899fc3b..87460d2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
@@ -50,8 +50,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } if (getClass() == other.getClass()) { final OperatorPredicate<?> p = (OperatorPredicate<?>) other; return getOperator().equals(p.getOperator())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java index 845c805..2432a41 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
@@ -92,8 +92,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } return getClass() == other.getClass() && getChildren().equals(((Predicate<?>) other).getChildren()); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java index bd1fa0c..b3c6aeb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
@@ -370,9 +370,6 @@ } catch (IllegalAccessException e) { throw error("Error in operator " + name + ":" + value, e); } catch (InvocationTargetException e) { - if (e.getCause() instanceof QueryParseException) { - throw (QueryParseException) e.getCause(); - } throw error("Error in operator " + name + ":" + value, e.getCause()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/VariablePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/VariablePredicate.java index d6d0f9c..e298e5f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/VariablePredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/VariablePredicate.java
@@ -81,8 +81,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } if (getClass() == other.getClass()) { final VariablePredicate<?> v = (VariablePredicate<?>) other; return getName().equals(v.getName())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/WildPatternPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/WildPatternPredicate.java index 48f3898..981051b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/WildPatternPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/WildPatternPredicate.java
@@ -45,8 +45,9 @@ @Override public boolean equals(final Object other) { - if (other == null) + if (other == null) { return false; + } if (getClass() == other.getClass()) { final WildPatternPredicate<?> p = (WildPatternPredicate<?>) other; return getOperator().equals(p.getOperator());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java index 1053d92..d167860 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
@@ -29,7 +29,7 @@ new InvalidProvider<InternalChangeQuery>(), new InvalidProvider<ChangeQueryRewriter>(), null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null)); + null, null, null, null, null, null, null, null, null)); private static final QueryRewriter.Definition<ChangeData, BasicChangeRewrites> mydef = new QueryRewriter.Definition<>(BasicChangeRewrites.class, BUILDER);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java index e4e94a1..d94921e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -119,7 +119,7 @@ public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes) throws OrmException { for (ChangeData cd : changes) { - cd.patches(); + cd.patchSets(); } } @@ -127,7 +127,7 @@ throws OrmException { Map<PatchSet.Id, ChangeData> missing = Maps.newHashMap(); for (ChangeData cd : changes) { - if (cd.currentPatchSet == null && cd.patches == null) { + if (cd.currentPatchSet == null && cd.patchSets == null) { missing.put(cd.change().currentPatchSetId(), cd); } } @@ -204,7 +204,7 @@ private String commitMessage; private List<FooterLine> commitFooters; private PatchSet currentPatchSet; - private Collection<PatchSet> patches; + private Collection<PatchSet> patchSets; private ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals; private List<PatchSetApproval> currentApprovals; private Map<Integer, List<String>> files = new HashMap<>(); @@ -450,6 +450,10 @@ return change; } + public void setChange(Change c) { + change = c; + } + public Change reloadChange() throws OrmException { change = db.changes().get(legacyId); return change; @@ -468,7 +472,7 @@ if (c == null) { return null; } - for (PatchSet p : patches()) { + for (PatchSet p : patchSets()) { if (p.getId().equals(c.currentPatchSetId())) { currentPatchSet = p; return p; @@ -536,23 +540,28 @@ * @return patches for the change. * @throws OrmException an error occurred reading the database. */ - public Collection<PatchSet> patches() + public Collection<PatchSet> patchSets() throws OrmException { - if (patches == null) { - patches = db.patchSets().byChange(legacyId).toList(); + if (patchSets == null) { + patchSets = db.patchSets().byChange(legacyId).toList(); } - return patches; + return patchSets; + } + + public void setPatchSets(Collection<PatchSet> patchSets) { + this.currentPatchSet = null; + this.patchSets = patchSets; } /** - * @return patch with the given ID, or null if it does not exist. + * @return patch set with the given ID, or null if it does not exist. * @throws OrmException an error occurred reading the database. */ - public PatchSet patch(PatchSet.Id psId) throws OrmException { + public PatchSet patchSet(PatchSet.Id psId) throws OrmException { if (currentPatchSet != null && currentPatchSet.getId().equals(psId)) { return currentPatchSet; } - for (PatchSet ps : patches()) { + for (PatchSet ps : patchSets()) { if (ps.getId().equals(psId)) { return ps; } @@ -602,7 +611,7 @@ return submitRecords; } - public void setMergeable(boolean mergeable) { + public void setMergeable(Boolean mergeable) { this.mergeable = mergeable; } @@ -619,9 +628,7 @@ if (ps == null || !changeControl().isPatchVisible(ps, db)) { return null; } - Repository repo = null; - try { - repo = repoManager.openRepository(c.getProject()); + try (Repository repo = repoManager.openRepository(c.getProject())) { Ref ref = repo.getRef(c.getDest().get()); SubmitTypeRecord rec = new SubmitRuleEvaluator(this) .getSubmitType(); @@ -637,10 +644,6 @@ ref, rec.type, mergeStrategy, c.getDest(), repo, db); } catch (IOException e) { throw new OrmException(e); - } finally { - if (repo != null) { - repo.close(); - } } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index c9d7e6cd..a0a7a3c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -15,17 +15,17 @@ package com.google.gerrit.server.query.change; import static com.google.gerrit.server.query.change.ChangeData.asChanges; +import static com.google.gerrit.server.query.change.InternalChangeQuery.schema; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.collect.Lists; -import com.google.gerrit.common.Nullable; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.errors.NotSignedInException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; @@ -34,8 +34,11 @@ import com.google.gerrit.server.account.CapabilityControl; import com.google.gerrit.server.account.GroupBackend; import com.google.gerrit.server.account.GroupBackends; +import com.google.gerrit.server.account.VersionedAccountQueries; import com.google.gerrit.server.change.ChangeTriplet; import com.google.gerrit.server.config.AllProjectsName; +import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.TrackingFooters; import com.google.gerrit.server.git.GitRepositoryManager; @@ -43,7 +46,6 @@ import com.google.gerrit.server.index.ChangeIndex; import com.google.gerrit.server.index.FieldDef; import com.google.gerrit.server.index.IndexCollection; -import com.google.gerrit.server.index.Schema; import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ListChildProjects; @@ -57,9 +59,13 @@ import com.google.inject.ProvisionException; import com.google.inject.util.Providers; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Repository; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -88,6 +94,7 @@ public static final String FIELD_BRANCH = "branch"; public static final String FIELD_CHANGE = "change"; public static final String FIELD_COMMENT = "comment"; + public static final String FIELD_COMMENTBY = "commentby"; public static final String FIELD_COMMIT = "commit"; public static final String FIELD_CONFLICTS = "conflicts"; public static final String FIELD_DELETED = "deleted"; @@ -107,6 +114,7 @@ public static final String FIELD_PATH = "path"; public static final String FIELD_PROJECT = "project"; public static final String FIELD_PROJECTS = "projects"; + public static final String FIELD_QUERY = "query"; public static final String FIELD_REF = "ref"; public static final String FIELD_REVIEWER = "reviewer"; public static final String FIELD_REVIEWERIN = "reviewerin"; @@ -138,6 +146,7 @@ final AccountResolver accountResolver; final GroupBackend groupBackend; final AllProjectsName allProjectsName; + final AllUsersNameProvider allUsersName; final PatchListCache patchListCache; final GitRepositoryManager repoManager; final ProjectCache projectCache; @@ -165,6 +174,7 @@ AccountResolver accountResolver, GroupBackend groupBackend, AllProjectsName allProjectsName, + AllUsersNameProvider allUsersName, PatchListCache patchListCache, GitRepositoryManager repoManager, ProjectCache projectCache, @@ -177,9 +187,9 @@ this(db, queryProvider, rewriter, userFactory, self, capabilityControlFactory, changeControlGenericFactory, changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend, - allProjectsName, patchListCache, repoManager, projectCache, - listChildProjects, indexes, submitStrategyFactory, conflictsCache, - trackingFooters, + allProjectsName, allUsersName, patchListCache, repoManager, + projectCache, listChildProjects, indexes, submitStrategyFactory, + conflictsCache, trackingFooters, cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true)); } @@ -197,6 +207,7 @@ AccountResolver accountResolver, GroupBackend groupBackend, AllProjectsName allProjectsName, + AllUsersNameProvider allUsersName, PatchListCache patchListCache, GitRepositoryManager repoManager, ProjectCache projectCache, @@ -219,6 +230,7 @@ this.accountResolver = accountResolver; this.groupBackend = groupBackend; this.allProjectsName = allProjectsName; + this.allUsersName = allUsersName; this.patchListCache = patchListCache; this.repoManager = repoManager; this.projectCache = projectCache; @@ -235,9 +247,9 @@ Providers.of(otherUser), capabilityControlFactory, changeControlGenericFactory, changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend, - allProjectsName, patchListCache, repoManager, projectCache, - listChildProjects, indexes, submitStrategyFactory, conflictsCache, - trackingFooters, allowsDrafts); + allProjectsName, allUsersName, patchListCache, repoManager, + projectCache, listChildProjects, indexes, submitStrategyFactory, + conflictsCache, trackingFooters, allowsDrafts); } Arguments asUser(Account.Id otherId) { @@ -426,8 +438,9 @@ @Operator public Predicate<ChangeData> project(String name) { - if (name.startsWith("^")) + if (name.startsWith("^")) { return new RegexProjectPredicate(name); + } return new ProjectPredicate(name); } @@ -444,15 +457,10 @@ @Operator public Predicate<ChangeData> branch(String name) { - if (name.startsWith("^")) - return ref("^" + branchToRef(name.substring(1))); - return ref(branchToRef(name)); - } - - private static String branchToRef(String name) { - if (!name.startsWith(Branch.R_HEADS)) - return Branch.R_HEADS + name; - return name; + if (name.startsWith("^")) { + return ref("^" + RefNames.fullName(name.substring(1))); + } + return ref(RefNames.fullName(name)); } @Operator @@ -462,15 +470,17 @@ @Operator public Predicate<ChangeData> topic(String name) { - if (name.startsWith("^")) - return new RegexTopicPredicate(name); - return new TopicPredicate(name); + if (name.startsWith("^")) { + return new RegexTopicPredicate(schema(args.indexes), name); + } + return new TopicPredicate(schema(args.indexes), name); } @Operator public Predicate<ChangeData> ref(String ref) { - if (ref.startsWith("^")) + if (ref.startsWith("^")) { return new RegexRefPredicate(ref); + } return new RefPredicate(ref); } @@ -659,9 +669,12 @@ @Operator public Predicate<ChangeData> owner(String who) throws QueryParseException, OrmException { - Set<Account.Id> m = parseAccount(who); - List<OwnerPredicate> p = Lists.newArrayListWithCapacity(m.size()); - for (Account.Id id : m) { + return owner(parseAccount(who)); + } + + private Predicate<ChangeData> owner(Set<Account.Id> who) { + List<OwnerPredicate> p = Lists.newArrayListWithCapacity(who.size()); + for (Account.Id id : who) { p.add(new OwnerPredicate(id)); } return Predicate.or(p); @@ -743,6 +756,46 @@ return new DeltaPredicate(value); } + @Operator + public Predicate<ChangeData> commentby(String who) + throws QueryParseException, OrmException { + return commentby(parseAccount(who)); + } + + private Predicate<ChangeData> commentby(Set<Account.Id> who) { + List<CommentByPredicate> p = Lists.newArrayListWithCapacity(who.size()); + for (Account.Id id : who) { + p.add(new CommentByPredicate(id)); + } + return Predicate.or(p); + } + + @Operator + public Predicate<ChangeData> from(String who) + throws QueryParseException, OrmException { + Set<Account.Id> ownerIds = parseAccount(who); + return Predicate.or(owner(ownerIds), commentby(ownerIds)); + } + + @Operator + public Predicate<ChangeData> query(String name) throws QueryParseException { + AllUsersName allUsers = args.allUsersName.get(); + try (Repository git = args.repoManager.openRepository(allUsers)) { + VersionedAccountQueries q = VersionedAccountQueries.forUser(self()); + q.load(git); + String query = q.getQueryList().getQuery(name); + if (query != null) { + return parse(query); + } + } catch (RepositoryNotFoundException e) { + throw new QueryParseException("Unknown named query (no " + + allUsers.get() +" repo): " + name, e); + } catch (IOException | ConfigInvalidException e) { + throw new QueryParseException("Error parsing named query: " + name, e); + } + throw new QueryParseException("Unknown named query: " + name); + } + @Override protected Predicate<ChangeData> defaultField(String query) throws QueryParseException { if (query.startsWith("refs/")) { @@ -834,9 +887,4 @@ private Account.Id self() throws QueryParseException { return args.getIdentifiedUser().getAccountId(); } - - private static Schema<ChangeData> schema(@Nullable IndexCollection indexes) { - ChangeIndex index = indexes != null ? indexes.getSearchIndex() : null; - return index != null ? index.getSchema() : null; - } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java new file mode 100644 index 0000000..dee7086 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -0,0 +1,57 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.query.change; + +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.ChangeMessage; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.server.index.ChangeField; +import com.google.gerrit.server.index.IndexPredicate; +import com.google.gwtorm.server.OrmException; + +import java.util.Objects; + +class CommentByPredicate extends IndexPredicate<ChangeData> { + private final Account.Id id; + + CommentByPredicate(Account.Id id) { + super(ChangeField.COMMENTBY, id.toString()); + this.id = id; + } + + Account.Id getAccountId() { + return id; + } + + @Override + public boolean match(ChangeData cd) throws OrmException { + for (ChangeMessage m : cd.messages()) { + if (Objects.equals(m.getAuthor(), id)) { + return true; + } + } + for (PatchLineComment c : cd.publishedComments()) { + if (Objects.equals(c.getAuthor(), id)) { + return true; + } + } + return false; + } + + @Override + public int getCost() { + return 1; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java index 14daa4d..3dd7c61 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -32,7 +32,7 @@ @Override public boolean match(final ChangeData object) throws OrmException { - for (PatchSet p : object.patches()) { + for (PatchSet p : object.patchSets()) { if (p.getRevision() != null && p.getRevision().get() != null) { final ObjectId id = ObjectId.fromString(p.getRevision().get()); if (abbrevId.prefixCompare(id) == 0) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java index e08847a..573bc77 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -17,14 +17,21 @@ import static com.google.gerrit.server.query.Predicate.and; import static com.google.gerrit.server.query.change.ChangeStatusPredicate.open; +import com.google.gerrit.common.Nullable; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.index.ChangeIndex; +import com.google.gerrit.server.index.IndexCollection; +import com.google.gerrit.server.index.Schema; import com.google.gerrit.server.query.Predicate; import com.google.gerrit.server.query.QueryParseException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; +import org.eclipse.jgit.lib.AbbreviatedObjectId; +import org.eclipse.jgit.lib.ObjectId; + import java.util.List; /** @@ -52,15 +59,18 @@ return new ChangeStatusPredicate(status); } - private static Predicate<ChangeData> topic(String topic) { - return new TopicPredicate(topic); + private static Predicate<ChangeData> commit(AbbreviatedObjectId id) { + return new CommitPredicate(id); } private final QueryProcessor qp; + private final IndexCollection indexes; @Inject - InternalChangeQuery(QueryProcessor queryProcessor) { + InternalChangeQuery(QueryProcessor queryProcessor, + IndexCollection indexes) { qp = queryProcessor.enforceVisibility(false); + this.indexes = indexes; } public InternalChangeQuery setLimit(int n) { @@ -73,6 +83,10 @@ return this; } + private Predicate<ChangeData> topic(String topic) { + return new TopicPredicate(schema(indexes), topic); + } + public List<ChangeData> byKey(Change.Key key) throws OrmException { return byKeyPrefix(key.get()); } @@ -123,6 +137,14 @@ return query(and(topic(topic), open())); } + public List<ChangeData> byCommitPrefix(String prefix) throws OrmException { + return query(commit(AbbreviatedObjectId.fromString(prefix))); + } + + public List<ChangeData> byCommit(ObjectId id) throws OrmException { + return query(commit(AbbreviatedObjectId.fromObjectId(id))); + } + private List<ChangeData> query(Predicate<ChangeData> p) throws OrmException { try { return qp.queryChanges(p).changes(); @@ -130,4 +152,9 @@ throw new OrmException(e); } } + + static Schema<ChangeData> schema(@Nullable IndexCollection indexes) { + ChangeIndex index = indexes != null ? indexes.getSearchIndex() : null; + return index != null ? index.getSchema() : null; + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java index 222c2bb..f73e0e4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -209,11 +209,11 @@ if (includePatchSets) { if (includeFiles) { - eventFactory.addPatchSets(c, d.patches(), + eventFactory.addPatchSets(c, d.patchSets(), includeApprovals ? d.approvals().asMap() : null, includeFiles, d.change(), labelTypes); } else { - eventFactory.addPatchSets(c, d.patches(), + eventFactory.addPatchSets(c, d.patchSets(), includeApprovals ? d.approvals().asMap() : null, labelTypes); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java index 51d971d..6068dd0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -132,6 +132,13 @@ if (limit == getBackendSupportedLimit()) { limit--; } + + int page = (start / limit) + 1; + if (page > indexConfig.maxPages()) { + throw new QueryParseException( + "Cannot go beyond page " + indexConfig.maxPages() + "of results"); + } + Predicate<ChangeData> s = queryRewriter.rewrite(q, start, limit + 1); if (!(s instanceof ChangeDataSource)) { q = Predicate.and(open(), q);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java index 3a9604f..7d5f1dc 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
@@ -15,8 +15,8 @@ package com.google.gerrit.server.query.change; import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.server.index.ChangeField; import com.google.gerrit.server.index.RegexPredicate; +import com.google.gerrit.server.index.Schema; import com.google.gwtorm.server.OrmException; import dk.brics.automaton.RegExp; @@ -25,8 +25,8 @@ class RegexTopicPredicate extends RegexPredicate<ChangeData> { private final RunAutomaton pattern; - RegexTopicPredicate(String re) { - super(ChangeField.TOPIC, re); + RegexTopicPredicate(Schema<ChangeData> schema, String re) { + super(TopicPredicate.topicField(schema), re); if (re.startsWith("^")) { re = re.substring(1);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RevWalkPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RevWalkPredicate.java deleted file mode 100644 index 0947fae..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RevWalkPredicate.java +++ /dev/null
@@ -1,117 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.query.change; - -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RevId; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.query.OperatorPredicate; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Provider; - -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevWalk; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -/** - * Predicate which creates Repository, RevWalk objects and properly - * closes them. Git based operators should extend this predicate. - * - */ -public abstract class RevWalkPredicate extends OperatorPredicate<ChangeData> { - private static final Logger log = - LoggerFactory.getLogger(RevWalkPredicate.class); - - public static class Arguments { - public final PatchSet patchSet; - public final RevId revision; - public final AnyObjectId objectId; - public final Change change; - public final Project.NameKey projectName; - - public Arguments(PatchSet patchSet, - RevId revision, - AnyObjectId objectId, - Change change, - Project.NameKey projectName) { - this.patchSet = patchSet; - this.revision = revision; - this.objectId = objectId; - this.change = change; - this.projectName = projectName; - } - } - - public final Provider<ReviewDb> db; - public final GitRepositoryManager repoManager; - - public RevWalkPredicate(Provider<ReviewDb> db, - GitRepositoryManager repoManager, String operator, String ref) { - super(operator, ref); - this.db = db; - this.repoManager = repoManager; - } - - @Override - public boolean match(ChangeData object) throws OrmException { - final PatchSet patchSet = object.currentPatchSet(); - if (patchSet == null) { - return false; - } - - final RevId revision = patchSet.getRevision(); - if (revision == null) { - return false; - } - - final AnyObjectId objectId = ObjectId.fromString(revision.get()); - if (objectId == null) { - return false; - } - - Change change = object.change(); - if (change == null) { - return false; - } - - final Project.NameKey projectName = change.getProject(); - if (projectName == null) { - return false; - } - - Arguments args = new Arguments(patchSet, revision, objectId, change, projectName); - - try (Repository repo = repoManager.openRepository(projectName); - RevWalk rw = new RevWalk(repo)) { - return match(repo, rw, args); - } catch (RepositoryNotFoundException e) { - log.error("Repository \"" + projectName.get() + "\" unknown.", e); - } catch (IOException e) { - log.error(projectName.get() + " cannot be read as a repository", e); - } - return false; - } - - public abstract boolean match(Repository repo, RevWalk rw, Arguments args); -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TopicPredicate.java index 07a6714..7196c9f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TopicPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TopicPredicate.java
@@ -18,12 +18,26 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.server.index.ChangeField; +import com.google.gerrit.server.index.FieldDef; import com.google.gerrit.server.index.IndexPredicate; +import com.google.gerrit.server.index.Schema; import com.google.gwtorm.server.OrmException; class TopicPredicate extends IndexPredicate<ChangeData> { - TopicPredicate(String topic) { - super(ChangeField.TOPIC, topic); + @SuppressWarnings("deprecation") + static FieldDef<ChangeData, ?> topicField(Schema<ChangeData> schema) { + if (schema == null) { + return ChangeField.LEGACY_TOPIC; + } + FieldDef<ChangeData, ?> f = schema.getFields().get(TOPIC.getName()); + if (f != null) { + return f; + } + return schema.getFields().get(ChangeField.LEGACY_TOPIC.getName()); + } + + TopicPredicate(Schema<ChangeData> schema, String topic) { + super(topicField(schema), topic); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java new file mode 100644 index 0000000..ced8cd0 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java
@@ -0,0 +1,59 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.schema; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.LabelType; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.server.git.ProjectConfig; + +public class AclUtil { + public static void grant(ProjectConfig config, AccessSection section, + String permission, GroupReference... groupList) { + grant(config, section, permission, false, groupList); + } + + public static void grant(ProjectConfig config, AccessSection section, + String permission, boolean force, GroupReference... groupList) { + Permission p = section.getPermission(permission, true); + for (GroupReference group : groupList) { + if (group != null) { + PermissionRule r = rule(config, group); + r.setForce(force); + p.add(r); + } + } + } + + public static void grant(ProjectConfig config, + AccessSection section, LabelType type, + int min, int max, GroupReference... groupList) { + String name = Permission.LABEL + type.getName(); + Permission p = section.getPermission(name, true); + for (GroupReference group : groupList) { + if (group != null) { + PermissionRule r = rule(config, group); + r.setRange(min, max); + p.add(r); + } + } + } + + public static PermissionRule rule(ProjectConfig config, GroupReference group) { + return new PermissionRule(config.resolve(group)); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java index 1eefbf9..1198176 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -17,6 +17,8 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; +import static com.google.gerrit.server.schema.AclUtil.grant; +import static com.google.gerrit.server.schema.AclUtil.rule; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; @@ -183,41 +185,6 @@ config.commitToNewRef(md, RefNames.REFS_CONFIG); } - private void grant(ProjectConfig config, AccessSection section, - String permission, GroupReference... groupList) { - grant(config, section, permission, false, groupList); - } - - private void grant(ProjectConfig config, AccessSection section, - String permission, boolean force, GroupReference... groupList) { - Permission p = section.getPermission(permission, true); - for (GroupReference group : groupList) { - if (group != null) { - PermissionRule r = rule(config, group); - r.setForce(force); - p.add(r); - } - } - } - - private void grant(ProjectConfig config, - AccessSection section, LabelType type, - int min, int max, GroupReference... groupList) { - String name = Permission.LABEL + type.getName(); - Permission p = section.getPermission(name, true); - for (GroupReference group : groupList) { - if (group != null) { - PermissionRule r = rule(config, group); - r.setRange(min, max); - p.add(r); - } - } - } - - private PermissionRule rule(ProjectConfig config, GroupReference group) { - return new PermissionRule(config.resolve(group)); - } - public static LabelType initCodeReviewLabel(ProjectConfig c) { LabelType type = new LabelType("Code-Review", ImmutableList.of( new LabelValue((short) 2, "Looks good to me, approved"), @@ -226,6 +193,7 @@ new LabelValue((short) -1, "I would prefer this is not merged as is"), new LabelValue((short) -2, "This shall not be merged"))); type.setCopyMinScore(true); + type.setCopyAllScoresOnTrivialRebase(true); c.getLabelSections().put(type.getName(), type); return type; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java index fda5306..3fa7986 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -14,8 +14,11 @@ package com.google.gerrit.server.schema; +import static com.google.gerrit.server.schema.AclUtil.grant; + import com.google.gerrit.common.Version; import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.Permission; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; @@ -40,6 +43,8 @@ private final AllUsersName allUsersName; private final PersonIdent serverUser; + private GroupReference admin; + @Inject AllUsersCreator( GitRepositoryManager mgr, @@ -50,6 +55,11 @@ this.serverUser = serverUser; } + public AllUsersCreator setAdministrators(GroupReference admin) { + this.admin = admin; + return this; + } + public void create() throws IOException, ConfigInvalidException { Repository git = null; try { @@ -84,8 +94,17 @@ Project project = config.getProject(); project.setDescription("Individual user settings and preferences."); - AccessSection all = config.getAccessSection(RefNames.REFS_USER + "*", true); + AccessSection all = config.getAccessSection(RefNames.REFS_USERS + "*", true); all.getPermission(Permission.READ, true).setExclusiveGroup(true); + + AccessSection defaults = config.getAccessSection(RefNames.REFS_USERS_DEFAULT, true); + defaults.getPermission(Permission.READ, true).setExclusiveGroup(true); + grant(config, defaults, Permission.READ, admin); + defaults.getPermission(Permission.PUSH, true).setExclusiveGroup(true); + grant(config, defaults, Permission.PUSH, admin); + defaults.getPermission(Permission.CREATE, true).setExclusiveGroup(true); + grant(config, defaults, Permission.CREATE, admin); + config.commit(md); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java index f43530f..66f2f1d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java
@@ -20,9 +20,6 @@ import org.eclipse.jgit.lib.Config; -import java.io.File; -import java.io.IOException; - class H2 extends BaseDataSourceType { protected final Config cfg; @@ -41,12 +38,6 @@ if (database == null || database.isEmpty()) { database = "db/ReviewDB"; } - File db = site.resolve(database); - try { - db = db.getCanonicalFile(); - } catch (IOException e) { - db = db.getAbsoluteFile(); - } - return "jdbc:h2:" + db.toURI().toString(); + return "jdbc:h2:" + site.resolve(database).toUri().toString(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java index daf1d4d..2581d56 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -32,14 +32,15 @@ import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.PersonIdent; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; /** Creates the current database schema and populates initial code rows. */ public class SchemaCreator { - private final @SitePath - File site_path; + @SitePath + private final + Path site_path; private final AllProjectsCreator allProjectsCreator; private final AllUsersCreator allUsersCreator; @@ -58,7 +59,7 @@ this(site.site_path, ap, auc, au, dst); } - public SchemaCreator(@SitePath File site, + public SchemaCreator(@SitePath Path site, AllProjectsCreator ap, AllUsersCreator auc, @GerritPersonIdent PersonIdent au, @@ -86,7 +87,9 @@ .setAdministrators(GroupReference.forGroup(admin)) .setBatchUsers(GroupReference.forGroup(batch)) .create(); - allUsersCreator.create(); + allUsersCreator + .setAdministrators(GroupReference.forGroup(admin)) + .create(); dataSourceType.getIndexScript().run(db); } @@ -117,9 +120,9 @@ final SystemConfig s = SystemConfig.create(); try { - s.sitePath = site_path.getCanonicalPath(); + s.sitePath = site_path.toRealPath().normalize().toString(); } catch (IOException e) { - s.sitePath = site_path.getAbsolutePath(); + s.sitePath = site_path.toAbsolutePath().normalize().toString(); } c.systemConfig().insert(Collections.singleton(s)); return s;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java index 2b9d4b4..fe5b992 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -131,9 +131,9 @@ throw new OrmException("No record in system_config table"); } try { - sc.sitePath = site.site_path.getCanonicalPath(); + sc.sitePath = site.site_path.toRealPath().normalize().toString(); } catch (IOException e) { - sc.sitePath = site.site_path.getAbsolutePath(); + sc.sitePath = site.site_path.toAbsolutePath().normalize().toString(); } db.systemConfig().update(Collections.singleton(sc)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java index 945baa8..e7359fd 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -49,8 +49,9 @@ public static int guessVersion(Class<?> c) { String n = c.getName(); n = n.substring(n.lastIndexOf('_') + 1); - while (n.startsWith("0")) + while (n.startsWith("0")) { n = n.substring(1); + } return Integer.parseInt(n); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java index 591601d..c809af4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java
@@ -58,14 +58,14 @@ throw new ProvisionException("Schema not yet initialized." + " Run init to initialize the schema:\n" + "$ java -jar gerrit.war init -d " - + site.site_path.getAbsolutePath()); + + site.site_path.toAbsolutePath()); } if (currentVer.versionNbr < expectedVer) { throw new ProvisionException("Unsupported schema version " + currentVer.versionNbr + "; expected schema version " + expectedVer + ". Run init to upgrade:\n" - + "$ java -jar " + site.gerrit_war.getAbsolutePath() + " init -d " - + site.site_path.getAbsolutePath()); + + "$ java -jar " + site.gerrit_war.toAbsolutePath() + " init -d " + + site.site_path.toAbsolutePath()); } else if (currentVer.versionNbr > expectedVer) { throw new ProvisionException("Unsupported schema version " + currentVer.versionNbr + "; expected schema version " + expectedVer
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java index b852217..7665c64 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java
@@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -35,8 +36,8 @@ @Inject DefaultSecureStore(SitePaths site) { - File secureConfig = new File(site.etc_dir, "secure.config"); - sec = new FileBasedConfig(secureConfig, FS.DETECTED); + Path secureConfig = site.etc_dir.resolve("secure.config"); + sec = new FileBasedConfig(secureConfig.toFile(), FS.DETECTED); try { sec.load(); } catch (Exception e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java index e830590..99127d8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java
@@ -26,14 +26,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; @Singleton public class SecureStoreProvider implements Provider<SecureStore> { private static final Logger log = LoggerFactory .getLogger(SecureStoreProvider.class); - private final File libdir; + private final Path libdir; private final Injector injector; private final String className;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java index 5c5e2f9..92873d3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java
@@ -46,7 +46,7 @@ /** * Determine the range of values being requested in the given query. * - * @param rangeQuery the raw query, e.g. "added:>12345" + * @param rangeQuery the raw query, e.g. "{@code added:>12345}" * @param minValue the minimum possible value for the field, inclusive * @param maxValue the maximum possible value for the field, inclusive * @return the calculated {@link Range}, or null if the query is invalid @@ -83,7 +83,8 @@ */ public static Range getRange( String prefix, String test, int queryInt, int minValue, int maxValue) { - int min, max; + int min; + int max; switch (test) { case "=": default:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/RegexListSearcher.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/RegexListSearcher.java index 4b0fd35..0a99a8a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/util/RegexListSearcher.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/RegexListSearcher.java
@@ -75,7 +75,8 @@ public Iterable<T> search(List<T> list) { checkNotNull(list); - int begin, end; + int begin; + int end; if (0 < prefixLen) { // Assumes many consecutive elements may have the same prefix, so the cost
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java index 8970425..195a3e0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/SubmoduleSectionParser.java
@@ -14,18 +14,20 @@ package com.google.gerrit.server.util; +import com.google.common.collect.Sets; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.SubmoduleSubscription; -import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.project.ProjectCache; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; import org.eclipse.jgit.lib.BlobBasedConfig; import org.eclipse.jgit.lib.Constants; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; +import java.util.Set; /** * It parses from a configuration file submodule sections. @@ -45,22 +47,30 @@ * </pre> */ public class SubmoduleSectionParser { + + public interface Factory { + SubmoduleSectionParser create(BlobBasedConfig bbc, String thisServer, + Branch.NameKey superProjectBranch); + } + + private final ProjectCache projectCache; private final BlobBasedConfig bbc; private final String thisServer; private final Branch.NameKey superProjectBranch; - private final GitRepositoryManager repoManager; - public SubmoduleSectionParser(final BlobBasedConfig bbc, - final String thisServer, final Branch.NameKey superProjectBranch, - final GitRepositoryManager repoManager) { + @Inject + public SubmoduleSectionParser(ProjectCache projectCache, + @Assisted BlobBasedConfig bbc, + @Assisted String thisServer, + @Assisted Branch.NameKey superProjectBranch) { + this.projectCache = projectCache; this.bbc = bbc; this.thisServer = thisServer; this.superProjectBranch = superProjectBranch; - this.repoManager = repoManager; } - public List<SubmoduleSubscription> parseAllSections() { - List<SubmoduleSubscription> parsedSubscriptions = new ArrayList<>(); + public Set<SubmoduleSubscription> parseAllSections() { + Set<SubmoduleSubscription> parsedSubscriptions = Sets.newHashSet(); for (final String id : bbc.getSubsections("submodule")) { final SubmoduleSubscription subscription = parse(id); if (subscription != null) { @@ -91,8 +101,6 @@ // Subscription really related to this running server. if (branch.equals(".")) { branch = superProjectBranch.get(); - } else if (!branch.startsWith(Constants.R_REFS)) { - branch = Constants.R_HEADS + branch; } final String urlExtractedPath = new URI(url).getPath(); @@ -106,12 +114,10 @@ projectName = projectName.substring(0, // projectName.length() - Constants.DOT_GIT_EXT.length()); } - - if (repoManager.list().contains(new Project.NameKey(projectName))) { - return new SubmoduleSubscription( - superProjectBranch, - new Branch.NameKey(new Project.NameKey(projectName), branch), - path); + Project.NameKey projectKey = new Project.NameKey(projectName); + if (projectCache.get(projectKey) != null) { + return new SubmoduleSubscription(superProjectBranch, + new Branch.NameKey(projectKey, branch), path); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java index cf7f11f..32cdca5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java
@@ -33,8 +33,8 @@ import org.eclipse.jgit.lib.Config; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; @Singleton public class SystemLog { @@ -56,12 +56,12 @@ return Strings.isNullOrEmpty(System.getProperty(LOG4J_CONFIGURATION)); } - public static Appender createAppender(File logdir, String name, Layout layout) { + public static Appender createAppender(Path logdir, String name, Layout layout) { final DailyRollingFileAppender dst = new DailyRollingFileAppender(); dst.setName(name); dst.setLayout(layout); dst.setEncoding("UTF-8"); - dst.setFile(new File(resolve(logdir), name).getPath()); + dst.setFile(resolve(logdir).resolve(name).toString()); dst.setImmediateFlush(true); dst.setAppend(true); dst.setErrorHandler(new DieErrorHandler()); @@ -91,11 +91,11 @@ return async; } - private static File resolve(final File logs_dir) { + private static Path resolve(Path p) { try { - return logs_dir.getCanonicalFile(); + return p.toRealPath().normalize(); } catch (IOException e) { - return logs_dir.getAbsoluteFile(); + return p.toAbsolutePath().normalize(); } }
diff --git a/gerrit-server/src/main/java/gerrit/AbstractCommitUserIdentityPredicate.java b/gerrit-server/src/main/java/gerrit/AbstractCommitUserIdentityPredicate.java index 606e883..32713d1 100644 --- a/gerrit-server/src/main/java/gerrit/AbstractCommitUserIdentityPredicate.java +++ b/gerrit-server/src/main/java/gerrit/AbstractCommitUserIdentityPredicate.java
@@ -17,11 +17,11 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.UserIdentity; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java b/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java index f0806a5..b9b6c5a 100644 --- a/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED__load_commit_labels_1.java
@@ -9,13 +9,13 @@ import com.google.gerrit.server.query.change.ChangeData; import com.google.gwtorm.server.OrmException; +import com.googlecode.prolog_cafe.exceptions.JavaException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; -import com.googlecode.prolog_cafe.lang.JavaException; import com.googlecode.prolog_cafe.lang.ListTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java index a955307..8efc2f1 100644 --- a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java +++ b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
@@ -20,15 +20,17 @@ import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.project.ChangeControl; -import com.googlecode.prolog_cafe.lang.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.PInstantiationException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; import com.googlecode.prolog_cafe.lang.Operation; -import com.googlecode.prolog_cafe.lang.PInstantiationException; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; +import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; +import com.googlecode.prolog_cafe.lang.VariableTerm; /** * Resolves the valid range for a label on a CurrentUser. @@ -54,18 +56,18 @@ Term a3 = arg3.dereference(); Term a4 = arg4.dereference(); - if (a1.isVariable()) { + if (a1 instanceof VariableTerm) { throw new PInstantiationException(this, 1); } - if (!a1.isSymbol()) { + if (!(a1 instanceof SymbolTerm)) { throw new IllegalTypeException(this, 1, "atom", a1); } String label = a1.name(); - if (a2.isVariable()) { + if (a2 instanceof VariableTerm) { throw new PInstantiationException(this, 2); } - if (!a2.isJavaObject() || !a2.convertible(CurrentUser.class)) { + if (!(a2 instanceof JavaObjectTerm) || !a2.convertible(CurrentUser.class)) { throw new IllegalTypeException(this, 2, "CurrentUser)", a2); } CurrentUser user = (CurrentUser) ((JavaObjectTerm) a2).object();
diff --git a/gerrit-server/src/main/java/gerrit/PRED_change_branch_1.java b/gerrit-server/src/main/java/gerrit/PRED_change_branch_1.java index b835b34..ee5bdc9 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_change_branch_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_change_branch_1.java
@@ -17,10 +17,10 @@ import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_change_owner_1.java b/gerrit-server/src/main/java/gerrit/PRED_change_owner_1.java index 51502f8..b56b036 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_change_owner_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_change_owner_1.java
@@ -17,11 +17,11 @@ import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_change_project_1.java b/gerrit-server/src/main/java/gerrit/PRED_change_project_1.java index 29c6704..e131605 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_change_project_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_change_project_1.java
@@ -17,10 +17,10 @@ import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_change_topic_1.java b/gerrit-server/src/main/java/gerrit/PRED_change_topic_1.java index 7ba648f..d1a91d9 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_change_topic_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_change_topic_1.java
@@ -17,10 +17,10 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_author_3.java b/gerrit-server/src/main/java/gerrit/PRED_commit_author_3.java index 3700909..59c4c18 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_author_3.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_author_3.java
@@ -18,9 +18,9 @@ import com.google.gerrit.reviewdb.client.UserIdentity; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.Term; public class PRED_commit_author_3 extends AbstractCommitUserIdentityPredicate {
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_committer_3.java b/gerrit-server/src/main/java/gerrit/PRED_commit_committer_3.java index 64823df..77a668b 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_committer_3.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_committer_3.java
@@ -18,9 +18,9 @@ import com.google.gerrit.reviewdb.client.UserIdentity; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.Term; public class PRED_commit_committer_3 extends AbstractCommitUserIdentityPredicate {
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_delta_4.java b/gerrit-server/src/main/java/gerrit/PRED_commit_delta_4.java index 51a871c..893c5bc 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_delta_4.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_delta_4.java
@@ -19,15 +19,16 @@ import com.google.gerrit.server.patch.PatchList; import com.google.gerrit.server.patch.PatchListEntry; -import com.googlecode.prolog_cafe.lang.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.PInstantiationException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; import com.googlecode.prolog_cafe.lang.Operation; -import com.googlecode.prolog_cafe.lang.PInstantiationException; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; +import com.googlecode.prolog_cafe.lang.VariableTerm; import java.util.Iterator; import java.util.regex.Pattern; @@ -66,22 +67,22 @@ engine.setB0(); Term a1 = arg1.dereference(); - if (a1.isVariable()) { + if (a1 instanceof VariableTerm) { throw new PInstantiationException(this, 1); } - if (!a1.isSymbol()) { + if (!(a1 instanceof SymbolTerm)) { throw new IllegalTypeException(this, 1, "symbol", a1); } Pattern regex = Pattern.compile(a1.name()); - engine.areg1 = new JavaObjectTerm(regex); - engine.areg2 = arg2; - engine.areg3 = arg3; - engine.areg4 = arg4; + engine.r1 = new JavaObjectTerm(regex); + engine.r2 = arg2; + engine.r3 = arg3; + engine.r4 = arg4; PatchList pl = StoredValues.PATCH_LIST.get(engine); Iterator<PatchListEntry> iter = pl.getPatches().iterator(); - engine.areg5 = new JavaObjectTerm(iter); + engine.r5 = new JavaObjectTerm(iter); return engine.jtry5(commit_delta_check, commit_delta_next); } @@ -89,11 +90,11 @@ private static final class PRED_commit_delta_check extends Operation { @Override public Operation exec(Prolog engine) { - Term a1 = engine.areg1; - Term a2 = engine.areg2; - Term a3 = engine.areg3; - Term a4 = engine.areg4; - Term a5 = engine.areg5; + Term a1 = engine.r1; + Term a2 = engine.r2; + Term a3 = engine.r3; + Term a4 = engine.r4; + Term a5 = engine.r5; Pattern regex = (Pattern)((JavaObjectTerm)a1).object(); @SuppressWarnings("unchecked") @@ -144,7 +145,7 @@ private static final class PRED_commit_delta_empty extends Operation { @Override public Operation exec(Prolog engine) { - Term a5 = engine.areg5; + Term a5 = engine.r5; @SuppressWarnings("unchecked") Iterator<PatchListEntry> iter =
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_edits_2.java b/gerrit-server/src/main/java/gerrit/PRED_commit_edits_2.java index 509faf0..c97a964 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_edits_2.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_edits_2.java
@@ -19,14 +19,16 @@ import com.google.gerrit.server.patch.PatchListEntry; import com.google.gerrit.server.patch.Text; -import com.googlecode.prolog_cafe.lang.IllegalTypeException; -import com.googlecode.prolog_cafe.lang.JavaException; +import com.googlecode.prolog_cafe.exceptions.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.JavaException; +import com.googlecode.prolog_cafe.exceptions.PInstantiationException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; -import com.googlecode.prolog_cafe.lang.PInstantiationException; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; +import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; +import com.googlecode.prolog_cafe.lang.VariableTerm; import org.eclipse.jgit.diff.Edit; import org.eclipse.jgit.errors.CorruptObjectException; @@ -134,10 +136,10 @@ } private Pattern getRegexParameter(Term term) { - if (term.isVariable()) { + if (term instanceof VariableTerm) { throw new PInstantiationException(this, 1); } - if (!term.isSymbol()) { + if (!(term instanceof SymbolTerm)) { throw new IllegalTypeException(this, 1, "symbol", term); } return Pattern.compile(term.name(), Pattern.MULTILINE);
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_message_1.java b/gerrit-server/src/main/java/gerrit/PRED_commit_message_1.java index e2eb6b1..6e1dc91 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_message_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_message_1.java
@@ -17,10 +17,10 @@ import com.google.gerrit.reviewdb.client.PatchSetInfo; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_commit_stats_3.java b/gerrit-server/src/main/java/gerrit/PRED_commit_stats_3.java index 83878be..4f665ee 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_commit_stats_3.java +++ b/gerrit-server/src/main/java/gerrit/PRED_commit_stats_3.java
@@ -17,11 +17,11 @@ import com.google.gerrit.rules.StoredValues; import com.google.gerrit.server.patch.PatchList; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.Term; /**
diff --git a/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java b/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java index 6d0dd0f..a63b1e7 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java
@@ -21,12 +21,12 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.PeerDaemonUser; -import com.googlecode.prolog_cafe.lang.EvaluationException; +import com.googlecode.prolog_cafe.exceptions.EvaluationException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_current_user_2.java b/gerrit-server/src/main/java/gerrit/PRED_current_user_2.java index 62fe075..3ee8d82 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_current_user_2.java +++ b/gerrit-server/src/main/java/gerrit/PRED_current_user_2.java
@@ -24,17 +24,18 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.inject.util.Providers; -import com.googlecode.prolog_cafe.lang.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.IllegalTypeException; +import com.googlecode.prolog_cafe.exceptions.PInstantiationException; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; import com.googlecode.prolog_cafe.lang.Operation; -import com.googlecode.prolog_cafe.lang.PInstantiationException; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term; +import com.googlecode.prolog_cafe.lang.VariableTerm; import java.util.Map; @@ -64,7 +65,7 @@ Term a1 = arg1.dereference(); Term a2 = arg2.dereference(); - if (a1.isVariable()) { + if (a1 instanceof VariableTerm) { throw new PInstantiationException(this, 1); } @@ -76,7 +77,7 @@ } public Term createUser(Prolog engine, Term key) { - if (!key.isStructure() + if (!(key instanceof StructureTerm) || key.arity() != 1 || !((StructureTerm) key).functor().equals(user)) { throw new IllegalTypeException(this, 1, "user(int)", key); @@ -84,7 +85,7 @@ Term idTerm = key.arg(0); CurrentUser user; - if (idTerm.isInteger()) { + if (idTerm instanceof IntegerTerm) { Map<Account.Id, IdentifiedUser> cache = StoredValues.USERS.get(engine); Account.Id accountId = new Account.Id(((IntegerTerm) idTerm).intValue()); user = cache.get(accountId);
diff --git a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java index 698c11c..f93e424 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -18,12 +18,12 @@ import com.google.gerrit.common.data.LabelValue; import com.google.gerrit.rules.StoredValues; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.IntegerTerm; import com.googlecode.prolog_cafe.lang.ListTerm; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.StructureTerm; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/java/gerrit/PRED_project_default_submit_type_1.java b/gerrit-server/src/main/java/gerrit/PRED_project_default_submit_type_1.java index 824c6ef..b1a8a74 100644 --- a/gerrit-server/src/main/java/gerrit/PRED_project_default_submit_type_1.java +++ b/gerrit-server/src/main/java/gerrit/PRED_project_default_submit_type_1.java
@@ -18,10 +18,10 @@ import com.google.gerrit.rules.StoredValues; import com.google.gerrit.server.project.ChangeControl; +import com.googlecode.prolog_cafe.exceptions.PrologException; import com.googlecode.prolog_cafe.lang.Operation; import com.googlecode.prolog_cafe.lang.Predicate; import com.googlecode.prolog_cafe.lang.Prolog; -import com.googlecode.prolog_cafe.lang.PrologException; import com.googlecode.prolog_cafe.lang.SymbolTerm; import com.googlecode.prolog_cafe.lang.Term;
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties b/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties index 3d8d271..2646fd0 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -33,6 +33,7 @@ pig = text/x-pig pl = text/x-perl pm = text/x-perl +pp = text/x-puppet project.config = text/x-ini properties = text/x-ini py = text/x-python
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java index f5dca09..74ab572 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -33,7 +33,8 @@ import com.google.gerrit.testutil.InMemoryRepositoryManager; import com.google.inject.AbstractModule; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; +import com.googlecode.prolog_cafe.exceptions.ReductionLimitException; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; import com.googlecode.prolog_cafe.lang.Prolog; import com.googlecode.prolog_cafe.lang.StructureTerm; @@ -112,7 +113,6 @@ public void testReductionLimit() throws CompileException { PrologEnvironment env = envFactory.create(machine); setUpEnvironment(env); - env.setEnabled(Prolog.Feature.IO, true); String script = "loopy :- b(5).\n" + "b(N) :- N > 0, !, S = N - 1, b(S).\n"
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java index aaab173..027d043 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java +++ b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
@@ -22,7 +22,7 @@ import com.google.inject.Guice; import com.google.inject.Module; -import com.googlecode.prolog_cafe.compiler.CompileException; +import com.googlecode.prolog_cafe.exceptions.CompileException; import com.googlecode.prolog_cafe.lang.BufferingPrologControl; import com.googlecode.prolog_cafe.lang.JavaObjectTerm; import com.googlecode.prolog_cafe.lang.Prolog; @@ -185,8 +185,8 @@ private Term removePackage(Term test) { Term name = test; - if (name.isStructure() && ":".equals(((StructureTerm) name).name())) { - name = ((StructureTerm) name).args()[1]; + if (name instanceof StructureTerm && ":".equals(name.name())) { + name = name.arg(1); } return name; }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java index 8bedd17..a437477 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -106,7 +106,8 @@ public Config config; @ConfigSuite.Config - public static @GerritServerConfig Config noteDbEnabled() { + @GerritServerConfig + public static Config noteDbEnabled() { return NotesMigration.allEnabledConfig(); } @@ -240,24 +241,23 @@ plc1 = newPatchLineComment(psId1, "Comment1", null, "FileOne.txt", Side.REVISION, 3, ownerId, timeBase, "First Comment", new CommentRange(1, 2, 3, 4)); - plc1.setRevId(new RevId("ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD")); + plc1.setRevId(new RevId("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd")); plc2 = newPatchLineComment(psId1, "Comment2", "Comment1", "FileOne.txt", Side.REVISION, 3, otherUserId, timeBase + 1000, "Reply to First Comment", new CommentRange(1, 2, 3, 4)); - plc2.setRevId(new RevId("ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD")); + plc2.setRevId(new RevId("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd")); plc3 = newPatchLineComment(psId1, "Comment3", "Comment1", "FileOne.txt", Side.PARENT, 3, ownerId, timeBase + 2000, "First Parent Comment", new CommentRange(1, 2, 3, 4)); - plc3.setRevId(new RevId("CDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEFCDEF")); + plc3.setRevId(new RevId("cdefcdefcdefcdefcdefcdefcdefcdefcdefcdef")); plc4 = newPatchLineComment(psId2, "Comment4", null, "FileOne.txt", Side.REVISION, 3, ownerId, timeBase + 3000, "Second Comment", new CommentRange(1, 2, 3, 4), Status.DRAFT); - plc4.setRevId(new RevId("BCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDE")); + plc4.setRevId(new RevId("bcdebcdebcdebcdebcdebcdebcdebcdebcdebcde")); plc5 = newPatchLineComment(psId2, "Comment5", null, "FileOne.txt", Side.REVISION, 5, ownerId, timeBase + 4000, "Third Comment", new CommentRange(3, 4, 5, 6), Status.DRAFT); - plc5.setRevId(new RevId("BCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDE")); - plc5.setRevId(new RevId("BCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDEBCDE")); + plc5.setRevId(new RevId("bcdebcdebcdebcdebcdebcdebcdebcdebcdebcde")); plc6 = newPatchLineComment(psId3, "Comment6", null, "FileOne.txt", Side.REVISION, 5, ownerId, timeBase + 5000, "Sixth Comment", new CommentRange(3, 4, 5, 6), Status.DRAFT); @@ -371,7 +371,7 @@ @Test public void testPatchLineCommentsUtilByCommentStatus() throws OrmException { assertThat(plcUtil.publishedByChange(db, revRes2.getNotes())) - .containsExactly(plc1, plc2, plc3).inOrder(); + .containsExactly(plc3, plc1, plc2).inOrder(); assertThat(plcUtil.draftByChange(db, revRes2.getNotes())) .containsExactly(plc4, plc5).inOrder(); } @@ -422,7 +422,7 @@ private void assertCommentMap(Map<String, List<CommentInfo>> actual, Map<String, ? extends List<PatchLineComment>> expected, boolean isPublished) { - assertThat((Iterable<?>)actual.keySet()).containsExactlyElementsIn(expected.keySet()); + assertThat(actual.keySet()).containsExactlyElementsIn(expected.keySet()); for (Map.Entry<String, List<CommentInfo>> entry : actual.entrySet()) { List<CommentInfo> actualList = entry.getValue(); List<PatchLineComment> expectedList = expected.get(entry.getKey()); @@ -443,7 +443,7 @@ assertThat(new Account.Id(ci.author._accountId)) .isEqualTo(plc.getAuthor()); } - assertThat((int) ci.line).isEqualTo(plc.getLine()); + assertThat(ci.line).isEqualTo(plc.getLine()); assertThat(MoreObjects.firstNonNull(ci.side, Side.REVISION)) .isEqualTo(plc.getSide() == 0 ? Side.PARENT : Side.REVISION); assertThat(TimeUtil.roundToSecond(ci.updated))
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java index 358620f..8caba88 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java
@@ -25,6 +25,7 @@ import com.google.gerrit.common.TimeUtil; import com.google.gerrit.extensions.api.changes.FixInput; import com.google.gerrit.extensions.common.ProblemInfo; +import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; @@ -36,10 +37,10 @@ import com.google.gerrit.testutil.FakeAccountByEmailCache; import com.google.gerrit.testutil.InMemoryDatabase; import com.google.gerrit.testutil.InMemoryRepositoryManager; +import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo; import com.google.gerrit.testutil.TestChanges; import com.google.inject.util.Providers; -import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; @@ -52,12 +53,13 @@ import java.util.List; public class ConsistencyCheckerTest { + private LifecycleManager lifecycle; private InMemoryDatabase schemaFactory; private ReviewDb db; private InMemoryRepositoryManager repoManager; private ConsistencyChecker checker; - private TestRepository<InMemoryRepository> repo; + private TestRepository<Repo> repo; private Project.NameKey project; private Account.Id userId; private RevCommit tip; @@ -65,7 +67,9 @@ @Before public void setUp() throws Exception { FakeAccountByEmailCache accountCache = new FakeAccountByEmailCache(); - schemaFactory = InMemoryDatabase.newDatabase(); + lifecycle = new LifecycleManager(); + schemaFactory = InMemoryDatabase.newDatabase(lifecycle); + lifecycle.start(); schemaFactory.create(); db = schemaFactory.open(); repoManager = new InMemoryRepositoryManager(); @@ -88,6 +92,9 @@ if (db != null) { db.close(); } + if (lifecycle != null) { + lifecycle.stop(); + } if (schemaFactory != null) { InMemoryDatabase.drop(schemaFactory); } @@ -335,7 +342,8 @@ @Test public void missingDestRef() throws Exception { - RefUpdate ru = repo.getRepository().updateRef("refs/heads/master"); + String ref = "refs/heads/master"; + RefUpdate ru = repo.getRepository().updateRef(ref); ru.setForceUpdate(true); assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED); Change c = insertChange(); @@ -344,7 +352,7 @@ updatePatchSetRef(ps); db.patchSets().insert(singleton(ps)); - assertProblems(c, "Destination ref not found (may be new branch): master"); + assertProblems(c, "Destination ref not found (may be new branch): " + ref); } @Test @@ -356,7 +364,8 @@ assertProblems(c, "Patch set 1 (" + rev + ") is not merged into destination ref" - + " master (" + tip.name() + "), but change status is MERGED"); + + " refs/heads/master (" + tip.name() + + "), but change status is MERGED"); } @Test @@ -370,7 +379,8 @@ assertProblems(c, "Patch set 1 (" + commit.name() + ") is merged into destination ref" - + " master (" + commit.name() + "), but change status is NEW"); + + " refs/heads/master (" + commit.name() + + "), but change status is NEW"); } @Test @@ -387,7 +397,8 @@ ProblemInfo p = problems.get(0); assertThat(p.message).isEqualTo( "Patch set 1 (" + commit.name() + ") is merged into destination ref" - + " master (" + commit.name() + "), but change status is NEW"); + + " refs/heads/master (" + commit.name() + + "), but change status is NEW"); assertThat(p.status).isEqualTo(ProblemInfo.Status.FIXED); assertThat(p.outcome).isEqualTo("Marked change as merged");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/HashtagsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/HashtagsTest.java index d5b722c..6100ffd 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/change/HashtagsTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/HashtagsTest.java
@@ -23,45 +23,45 @@ public class HashtagsTest { @Test public void emptyCommitMessage() throws Exception { - assertThat((Iterable<?>)HashtagsUtil.extractTags("")).isEmpty(); + assertThat(HashtagsUtil.extractTags("")).isEmpty(); } @Test public void nullCommitMessage() throws Exception { - assertThat((Iterable<?>)HashtagsUtil.extractTags(null)).isEmpty(); + assertThat(HashtagsUtil.extractTags(null)).isEmpty(); } @Test public void noHashtags() throws Exception { String commitMessage = "Subject\n\nLine 1\n\nLine 2"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)).isEmpty(); + assertThat(HashtagsUtil.extractTags(commitMessage)).isEmpty(); } @Test public void singleHashtag() throws Exception { String commitMessage = "#Subject\n\nLine 1\n\nLine 2"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("Subject")); } @Test public void singleHashtagNumeric() throws Exception { String commitMessage = "Subject\n\n#123\n\nLine 2"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("123")); } @Test public void multipleHashtags() throws Exception { String commitMessage = "#Subject\n\n#Hashtag\n\nLine 2"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("Subject", "Hashtag")); } @Test public void repeatedHashtag() throws Exception { String commitMessage = "#Subject\n\n#Hashtag1\n\n#Hashtag2\n\n#Hashtag1"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn( Sets.newHashSet("Subject", "Hashtag1", "Hashtag2")); } @@ -69,21 +69,21 @@ @Test public void multipleHashtagsNoSpaces() throws Exception { String commitMessage = "Subject\n\n#Hashtag1#Hashtag2"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("Hashtag1")); } @Test public void hyphenatedHashtag() throws Exception { String commitMessage = "Subject\n\n#Hyphenated-Hashtag"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("Hyphenated-Hashtag")); } @Test public void underscoredHashtag() throws Exception { String commitMessage = "Subject\n\n#Underscored_Hashtag"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn(Sets.newHashSet("Underscored_Hashtag")); } @@ -91,7 +91,7 @@ public void hashtagsWithAccentedCharacters() throws Exception { String commitMessage = "Jag #måste #öva på min #Svenska!\n\n" + "Jag behöver en #läkare."; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)) + assertThat(HashtagsUtil.extractTags(commitMessage)) .containsExactlyElementsIn( Sets.newHashSet("måste", "öva", "Svenska", "läkare")); } @@ -99,6 +99,6 @@ @Test public void hashWithoutHashtag() throws Exception { String commitMessage = "Subject\n\n# Text"; - assertThat((Iterable<?>)HashtagsUtil.extractTags(commitMessage)).isEmpty(); + assertThat(HashtagsUtil.extractTags(commitMessage)).isEmpty(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java new file mode 100644 index 0000000..1fb6d81 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/RepositoryConfigTest.java
@@ -0,0 +1,147 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.config; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.gerrit.extensions.client.SubmitType; +import com.google.gerrit.reviewdb.client.Project.NameKey; + +import org.eclipse.jgit.lib.Config; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +public class RepositoryConfigTest { + + private Config cfg; + private RepositoryConfig repoCfg; + + @Before + public void setUp() throws Exception { + cfg = new Config(); + repoCfg = new RepositoryConfig(cfg); + } + + @Test + public void testDefaultSubmitTypeWhenNotConfigured() { + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.MERGE_IF_NECESSARY); + } + + @Test + public void testDefaultSubmitTypeForStarFilter() { + configureDefaultSubmitType("*", SubmitType.CHERRY_PICK); + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.CHERRY_PICK); + + configureDefaultSubmitType("*", SubmitType.FAST_FORWARD_ONLY); + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.FAST_FORWARD_ONLY); + + configureDefaultSubmitType("*", SubmitType.REBASE_IF_NECESSARY); + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.REBASE_IF_NECESSARY); + } + + @Test + public void testDefaultSubmitTypeForSpecificFilter() { + configureDefaultSubmitType("someProject", SubmitType.CHERRY_PICK); + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someOtherProject"))) + .isEqualTo(SubmitType.MERGE_IF_NECESSARY); + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.CHERRY_PICK); + } + + @Test + public void testDefaultSubmitTypeForStartWithFilter() { + configureDefaultSubmitType("somePath/somePath/*", + SubmitType.REBASE_IF_NECESSARY); + configureDefaultSubmitType("somePath/*", SubmitType.CHERRY_PICK); + configureDefaultSubmitType("*", SubmitType.MERGE_ALWAYS); + + assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject"))) + .isEqualTo(SubmitType.MERGE_ALWAYS); + + assertThat( + repoCfg.getDefaultSubmitType(new NameKey("somePath/someProject"))) + .isEqualTo(SubmitType.CHERRY_PICK); + + assertThat( + repoCfg.getDefaultSubmitType(new NameKey( + "somePath/somePath/someProject"))).isEqualTo( + SubmitType.REBASE_IF_NECESSARY); + } + + private void configureDefaultSubmitType(String projectFilter, + SubmitType submitType) { + cfg.setString(RepositoryConfig.SECTION_NAME, projectFilter, + RepositoryConfig.DEFAULT_SUBMIT_TYPE_NAME, submitType.toString()); + } + + @Test + public void testOwnerGroupsWhenNotConfigured() { + assertThat(repoCfg.getOwnerGroups(new NameKey("someProject"))).isEqualTo( + new String[] {}); + } + + @Test + public void testOwnerGroupsForStarFilter() { + String[] ownerGroups = new String[] {"group1", "group2"}; + configureOwnerGroups("*", Lists.newArrayList(ownerGroups)); + assertThat(repoCfg.getOwnerGroups(new NameKey("someProject"))).isEqualTo( + ownerGroups); + } + + @Test + public void testOwnerGroupsForSpecificFilter() { + String[] ownerGroups = new String[] {"group1", "group2"}; + configureOwnerGroups("someProject", Lists.newArrayList(ownerGroups)); + assertThat(repoCfg.getOwnerGroups(new NameKey("someOtherProject"))) + .isEqualTo(new String[] {}); + assertThat(repoCfg.getOwnerGroups(new NameKey("someProject"))).isEqualTo( + ownerGroups); + } + + @Test + public void testOwnerGroupsForStartWithFilter() { + String[] ownerGroups1 = new String[] {"group1"}; + String[] ownerGroups2 = new String[] {"group2"}; + String[] ownerGroups3 = new String[] {"group3"}; + + configureOwnerGroups("*", Lists.newArrayList(ownerGroups1)); + configureOwnerGroups("somePath/*", Lists.newArrayList(ownerGroups2)); + configureOwnerGroups("somePath/somePath/*", + Lists.newArrayList(ownerGroups3)); + + assertThat(repoCfg.getOwnerGroups(new NameKey("someProject"))).isEqualTo( + ownerGroups1); + + assertThat(repoCfg.getOwnerGroups(new NameKey("somePath/someProject"))) + .isEqualTo(ownerGroups2); + + assertThat( + repoCfg.getOwnerGroups(new NameKey("somePath/somePath/someProject"))) + .isEqualTo(ownerGroups3); + } + + private void configureOwnerGroups(String projectFilter, + List<String> ownerGroups) { + cfg.setStringList(RepositoryConfig.SECTION_NAME, projectFilter, + RepositoryConfig.OWNER_GROUP_NAME, ownerGroups); + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java index 5fdecf0..5533d53 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
@@ -25,88 +25,89 @@ import org.junit.Test; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; public class SitePathsTest { @Test public void testCreate_NotExisting() throws IOException { - final File root = random(); + final Path root = random(); final SitePaths site = new SitePaths(root); assertTrue(site.isNew); assertEquals(root, site.site_path); - assertEquals(new File(root, "etc"), site.etc_dir); + assertEquals(root.resolve("etc"), site.etc_dir); } @Test public void testCreate_Empty() throws IOException { - final File root = random(); + final Path root = random(); try { - assertTrue(root.mkdir()); + Files.createDirectory(root); final SitePaths site = new SitePaths(root); assertTrue(site.isNew); assertEquals(root, site.site_path); } finally { - root.delete(); + Files.delete(root); } } @Test public void testCreate_NonEmpty() throws IOException { - final File root = random(); - final File txt = new File(root, "test.txt"); + final Path root = random(); + final Path txt = root.resolve("test.txt"); try { - assertTrue(root.mkdir()); - assertTrue(txt.createNewFile()); + Files.createDirectory(root); + Files.createFile(txt); final SitePaths site = new SitePaths(root); assertFalse(site.isNew); assertEquals(root, site.site_path); } finally { - txt.delete(); - root.delete(); + Files.delete(txt); + Files.delete(root); } } @Test public void testCreate_NotDirectory() throws IOException { - final File root = random(); + final Path root = random(); try { - assertTrue(root.createNewFile()); + Files.createFile(root); try { new SitePaths(root); fail("Did not throw exception"); - } catch (FileNotFoundException e) { - assertEquals("Not a directory: " + root.getPath(), e.getMessage()); + } catch (NotDirectoryException e) { + // Expected. } } finally { - root.delete(); + Files.delete(root); } } @Test public void testResolve() throws IOException { - final File root = random(); + final Path root = random(); final SitePaths site = new SitePaths(root); assertNull(site.resolve(null)); assertNull(site.resolve("")); assertNotNull(site.resolve("a")); - assertEquals(new File(root, "a").getCanonicalFile(), site.resolve("a")); + assertEquals(root.resolve("a").toAbsolutePath().normalize(), + site.resolve("a")); final String pfx = HostPlatform.isWin32() ? "C:/" : "/"; assertNotNull(site.resolve(pfx + "a")); - assertEquals(new File(pfx + "a").getCanonicalFile(), site.resolve(pfx + "a")); + assertEquals(Paths.get(pfx + "a"), site.resolve(pfx + "a")); } - private static File random() throws IOException { - File tmp = File.createTempFile("gerrit_test_", "_site"); - if (!tmp.delete()) { - throw new IOException("Cannot create " + tmp.getPath()); - } + private static Path random() throws IOException { + Path tmp = Files.createTempFile("gerrit_test_", "_site"); + Files.deleteIfExists(tmp); return tmp; } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java index e741a91c..fde86a5 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/GroupListTest.java
@@ -118,4 +118,8 @@ "ebe31c01aec2c9ac3b3c03e87a47450829ff4310"))); } + @Test + public void testAsText() throws Exception { + assertTrue(TEXT.equals(groupList.asText())); + } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java index 78a4c5c..181ba15 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/LabelNormalizerTest.java
@@ -46,6 +46,7 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.google.inject.util.Providers; import org.eclipse.jgit.lib.Repository; import org.junit.After; @@ -59,7 +60,7 @@ @Inject private AccountManager accountManager; @Inject private AllProjectsName allProjects; @Inject private GitRepositoryManager repoManager; - @Inject private IdentifiedUser.RequestFactory userFactory; + @Inject private IdentifiedUser.GenericFactory userFactory; @Inject private InMemoryDatabase schemaFactory; @Inject private LabelNormalizer norm; @Inject private MetaDataUpdate.User metaDataUpdateFactory; @@ -84,7 +85,7 @@ schemaCreator.create(db); userId = accountManager.authenticate(AuthRequest.forUser("user")) .getAccountId(); - user = userFactory.create(userId); + user = userFactory.create(Providers.of(db), userId); configureProject(); setUpChange();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java new file mode 100644 index 0000000..ca154b1 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
@@ -0,0 +1,233 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.notedb.NotesMigration; +import com.google.gerrit.testutil.TempFileUtil; +import com.google.gwtorm.client.KeyUtil; +import com.google.gwtorm.server.StandardKeyEncoder; + +import org.easymock.EasyMockSupport; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryCache; +import org.eclipse.jgit.lib.RepositoryCache.FileKey; +import org.eclipse.jgit.util.FS; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Path; + +public class LocalDiskRepositoryManagerTest extends EasyMockSupport { + + static { + KeyUtil.setEncoderImpl(new StandardKeyEncoder()); + } + + private Config cfg; + private SitePaths site; + private LocalDiskRepositoryManager repoManager; + + @Before + public void setUp() throws Exception { + site = new SitePaths(TempFileUtil.createTempDirectory().toPath()); + site.resolve("git").toFile().mkdir(); + cfg = new Config(); + cfg.setString("gerrit", null, "basePath", "git"); + repoManager = + new LocalDiskRepositoryManager(site, cfg, + createNiceMock(NotesMigration.class)); + } + + @Test(expected = IllegalStateException.class) + public void testThatNullBasePathThrowsAnException() { + new LocalDiskRepositoryManager(site, new Config(), + createNiceMock(NotesMigration.class)); + } + + @Test + public void testProjectCreation() throws Exception { + Project.NameKey projectA = new Project.NameKey("projectA"); + try (Repository repo = repoManager.createRepository(projectA)) { + assertThat(repo).isNotNull(); + } + try (Repository repo = repoManager.openRepository(projectA)) { + assertThat(repo).isNotNull(); + } + assertThat(repoManager.list()).containsExactly(projectA); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithEmptyName() throws Exception { + repoManager.createRepository(new Project.NameKey("")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithTrailingSlash() throws Exception { + repoManager.createRepository(new Project.NameKey("projectA/")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithBackSlash() throws Exception { + repoManager.createRepository(new Project.NameKey("a\\projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationAbsolutePath() throws Exception { + repoManager.createRepository(new Project.NameKey("/projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationStartingWithDotDot() throws Exception { + repoManager.createRepository(new Project.NameKey("../projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationContainsDotDot() throws Exception { + repoManager.createRepository(new Project.NameKey("a/../projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationDotPathSegment() throws Exception { + repoManager.createRepository(new Project.NameKey("a/./projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithTwoSlashes() throws Exception { + repoManager.createRepository(new Project.NameKey("a//projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithPathSegmentEndingByDotGit() + throws Exception { + repoManager.createRepository(new Project.NameKey("a/b.git/projectA")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithQuestionMark() throws Exception { + repoManager.createRepository(new Project.NameKey("project?A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithPercentageSign() throws Exception { + repoManager.createRepository(new Project.NameKey("project%A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithWidlcard() throws Exception { + repoManager.createRepository(new Project.NameKey("project*A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithColon() throws Exception { + repoManager.createRepository(new Project.NameKey("project:A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithLessThatSign() throws Exception { + repoManager.createRepository(new Project.NameKey("project<A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithGreaterThatSign() throws Exception { + repoManager.createRepository(new Project.NameKey("project>A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithPipe() throws Exception { + repoManager.createRepository(new Project.NameKey("project|A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithDollarSign() throws Exception { + repoManager.createRepository(new Project.NameKey("project$A")); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testProjectCreationWithCarriageReturn() throws Exception { + repoManager.createRepository(new Project.NameKey("project\\rA")); + } + + @Test + public void testOpenRepositoryCreatedDirectlyOnDisk() throws Exception { + createRepository(repoManager.getBasePath(), "projectA"); + Project.NameKey projectA = new Project.NameKey("projectA"); + try (Repository repo = repoManager.openRepository(projectA)) { + assertThat(repo).isNotNull(); + } + assertThat(repoManager.list()).containsExactly(projectA); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testOpenRepositoryInvalidName() throws Exception { + repoManager.openRepository(new Project.NameKey("project%?|<>A")); + } + + @Test + public void testList() throws Exception { + Project.NameKey projectA = new Project.NameKey("projectA"); + createRepository(repoManager.getBasePath(), projectA.get()); + + Project.NameKey projectB = new Project.NameKey("path/projectB"); + createRepository(repoManager.getBasePath(), projectB.get()); + + Project.NameKey projectC = new Project.NameKey("anotherPath/path/projectC"); + createRepository(repoManager.getBasePath(), projectC.get()); + // create an invalid git repo named only .git + repoManager.getBasePath().resolve(".git").toFile().mkdir(); + // create an invalid repo name + createRepository(repoManager.getBasePath(), "project?A"); + assertThat(repoManager.list()) + .containsExactly(projectA, projectB, projectC); + } + + @Test + public void testGetSetProjectDescription() throws Exception { + Project.NameKey projectA = new Project.NameKey("projectA"); + try (Repository repo = repoManager.createRepository(projectA)) { + assertThat(repo).isNotNull(); + } + + assertThat(repoManager.getProjectDescription(projectA)).isNull(); + repoManager.setProjectDescription(projectA, "projectA description"); + assertThat(repoManager.getProjectDescription(projectA)).isEqualTo( + "projectA description"); + + repoManager.setProjectDescription(projectA, ""); + assertThat(repoManager.getProjectDescription(projectA)).isNull(); + } + + @Test(expected = RepositoryNotFoundException.class) + public void testGetProjectDescriptionFromUnexistingRepository() + throws Exception { + repoManager.getProjectDescription(new Project.NameKey("projectA")); + } + + private void createRepository(Path directory, String projectName) + throws IOException { + String n = projectName + Constants.DOT_GIT_EXT; + FileKey loc = FileKey.exact(directory.resolve(n).toFile(), FS.DETECTED); + try (Repository db = RepositoryCache.open(loc, false)) { + db.create(true /* bare */); + } + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java new file mode 100644 index 0000000..d022d3e --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java
@@ -0,0 +1,121 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import static com.google.common.truth.Truth.assertThat; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.replay; + +import junit.framework.TestCase; + +import org.junit.Test; + +import java.io.IOException; + +public class QueryListTest extends TestCase { + public static final String Q_P = "project:foo"; + public static final String Q_B = "branch:bar"; + public static final String Q_COMPLEX = "branch:bar AND peers:'is:open\t'"; + + public static final String N_FOO = "foo"; + public static final String N_BAR = "bar"; + + public static final String L_FOO = N_FOO + "\t" + Q_P + "\n"; + public static final String L_BAR = N_BAR + "\t" + Q_B + "\n"; + public static final String L_FOO_PROP = N_FOO + " \t" + Q_P + "\n"; + public static final String L_BAR_PROP = N_BAR + " \t" + Q_B + "\n"; + public static final String L_FOO_PAD_F = " " + N_FOO + "\t" + Q_P + "\n"; + public static final String L_FOO_PAD_E = N_FOO + " \t" + Q_P + "\n"; + public static final String L_BAR_PAD_F = N_BAR + "\t " + Q_B + "\n"; + public static final String L_BAR_PAD_E = N_BAR + "\t" + Q_B + " \n"; + public static final String L_COMPLEX = N_FOO + "\t" + Q_COMPLEX + "\t \n"; + public static final String L_BAD = N_FOO + "\n"; + + public static final String HEADER = "# Name\tQuery\n"; + public static final String C1 = "# A Simple Comment\n"; + public static final String C2 = "# Comment with a tab\t and multi # # #\n"; + + public static final String F_SIMPLE = L_FOO + L_BAR; + public static final String F_PROPER = L_BAR_PROP + L_FOO_PROP; // alpha order + public static final String F_PAD_F = L_FOO_PAD_F + L_BAR_PAD_F; + public static final String F_PAD_E = L_FOO_PAD_E + L_BAR_PAD_E; + + @Test + public void testParseSimple() throws Exception { + QueryList ql = QueryList.parse(F_SIMPLE, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParseWHeader() throws Exception { + QueryList ql = QueryList.parse(HEADER + F_SIMPLE, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParseWComments() throws Exception { + QueryList ql = QueryList.parse(C1 + F_SIMPLE + C2, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParseFooComment() throws Exception { + QueryList ql = QueryList.parse("#" + L_FOO + L_BAR, null); + assertThat(ql.getQuery(N_FOO)).isNull(); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParsePaddedFronts() throws Exception { + QueryList ql = QueryList.parse(F_PAD_F, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParsePaddedEnds() throws Exception { + QueryList ql = QueryList.parse(F_PAD_E, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P); + assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B); + } + + @Test + public void testParseComplex() throws Exception { + QueryList ql = QueryList.parse(L_COMPLEX, null); + assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_COMPLEX); + } + + @Test(expected = IOException.class) + public void testParseBad() throws Exception { + ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class); + replay(sink); + QueryList.parse(L_BAD, sink); + } + + @Test + public void testAsText() throws Exception { + String expectedText = HEADER + "#\n" + F_PROPER; + QueryList ql = QueryList.parse(F_SIMPLE, null); + String asText = ql.asText(); + assertThat(asText).isEqualTo(expectedText); + + ql = QueryList.parse(asText, null); + asText = ql.asText(); + assertThat(asText).isEqualTo(expectedText); + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java deleted file mode 100644 index c2f3f6f..0000000 --- a/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java +++ /dev/null
@@ -1,1017 +0,0 @@ -// Copyright (C) 2011 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.git; - -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.junit.Assert.assertEquals; - -import com.google.gerrit.common.ChangeHooks; -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Branch; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.SubmoduleSubscription; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.reviewdb.server.SubmoduleSubscriptionAccess; -import com.google.gerrit.server.extensions.events.GitReferenceUpdated; -import com.google.gwtorm.client.KeyUtil; -import com.google.gwtorm.server.ListResultSet; -import com.google.gwtorm.server.ResultSet; -import com.google.gwtorm.server.SchemaFactory; -import com.google.gwtorm.server.StandardKeyEncoder; -import com.google.inject.Provider; - -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.easymock.IMocksControl; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.dircache.DirCacheBuilder; -import org.eclipse.jgit.dircache.DirCacheEntry; -import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; -import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileMode; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -public class SubmoduleOpTest extends LocalDiskRepositoryTestCase { - static { - KeyUtil.setEncoderImpl(new StandardKeyEncoder()); - } - - private static final String newLine = System.getProperty("line.separator"); - - private IMocksControl mockMaker; - private SchemaFactory<ReviewDb> schemaFactory; - private SubmoduleSubscriptionAccess subscriptions; - private ReviewDb schema; - private Provider<String> urlProvider; - private GitRepositoryManager repoManager; - private GitReferenceUpdated gitRefUpdated; - private ChangeHooks changeHooks; - - @SuppressWarnings("unchecked") - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - - mockMaker = EasyMock.createStrictControl(); - schemaFactory = mockMaker.createMock(SchemaFactory.class); - schema = mockMaker.createMock(ReviewDb.class); - subscriptions = mockMaker.createMock(SubmoduleSubscriptionAccess.class); - urlProvider = mockMaker.createMock(Provider.class); - repoManager = mockMaker.createMock(GitRepositoryManager.class); - gitRefUpdated = mockMaker.createMock(GitReferenceUpdated.class); - changeHooks = mockMaker.createMock(ChangeHooks.class); - } - - private void doReplay() { - mockMaker.replay(); - } - - private void doVerify() { - mockMaker.verify(); - } - - /** - * It tests Submodule.update in the scenario a merged commit is an empty one - * (it does not have a .gitmodules file) and the project the commit was merged - * is not a submodule of other project. - * - * @throws Exception If an exception occurs. - */ - @Test - public void testEmptyCommit() throws Exception { - expect(schemaFactory.open()).andReturn(schema); - - try (Repository realDb = createWorkRepository()) { - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git git = new Git(realDb); - - final RevCommit mergeTip = git.commit().setMessage("test").call(); - - final Branch.NameKey branchNameKey = - new Branch.NameKey(new Project.NameKey("test-project"), "test-branch"); - - expect(urlProvider.get()).andReturn("http://localhost:8080"); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - final ResultSet<SubmoduleSubscription> emptySubscriptions = - new ListResultSet<>(new ArrayList<SubmoduleSubscription>()); - expect(subscriptions.bySubmodule(branchNameKey)).andReturn( - emptySubscriptions); - - schema.close(); - - doReplay(); - - final SubmoduleOp submoduleOp = - new SubmoduleOp(branchNameKey, mergeTip, new RevWalk(realDb), urlProvider, - schemaFactory, realDb, null, new ArrayList<Change>(), null, null, - null, null, null, null); - - submoduleOp.update(); - - doVerify(); - } - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project"</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source"] - * path = source - * url = http://localhost:8080/source - * branch = . - * </pre> - * <p> - * It expects to insert a new row in subscriptions table. The row inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "a" on branch "refs/heads/master"</li> - * <li>path "a"</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionToDotBranchValue() throws Exception { - doOneSubscriptionInsert(buildSubmoduleSection("source", "source", - "http://localhost:8080/source", ".").toString(), "refs/heads/master"); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project"</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source"] - * path = source - * url = http://localhost:8080/source - * branch = refs/heads/master - * </pre> - * - * <p> - * It expects to insert a new row in subscriptions table. The row inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source" on branch "refs/heads/master"</li> - * <li>path "source"</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionToSameBranch() throws Exception { - doOneSubscriptionInsert(buildSubmoduleSection("source", "source", - "http://localhost:8080/source", "refs/heads/master").toString(), - "refs/heads/master"); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project"</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source"] - * path = source - * url = http://localhost:8080/source - * branch = refs/heads/test - * </pre> - * <p> - * It expects to insert a new row in subscriptions table. The row inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source" on branch "refs/heads/test"</li> - * <li>path "source"</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionToDifferentBranch() throws Exception { - doOneSubscriptionInsert(buildSubmoduleSection("source", "source", - "http://localhost:8080/source", "refs/heads/test").toString(), - "refs/heads/test"); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project" in "refs/heads/master" branch</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source-a"] - * path = source-a - * url = http://localhost:8080/source-a - * branch = . - * - * [submodule "source-b"] - * path = source-b - * url = http://localhost:8080/source-b - * branch = . - * </pre> - * <p> - * It expects to insert new rows in subscriptions table. The rows inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source-a" on branch "refs/heads/master" with "source-a" path</li> - * <li>source "source-b" on branch "refs/heads/master" with "source-b" path</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionsWithDotBranchValue() throws Exception { - final StringBuilder sb = - buildSubmoduleSection("source-a", "source-a", - "http://localhost:8080/source-a", "."); - sb.append(buildSubmoduleSection("source-b", "source-b", - "http://localhost:8080/source-b", ".")); - - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> subscriptionsToInsert = new ArrayList<>(); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-a"), "refs/heads/master"), "source-a")); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-b"), "refs/heads/master"), "source-b")); - - doOnlySubscriptionInserts(sb.toString(), mergedBranch, - subscriptionsToInsert); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project" in "refs/heads/master" branch</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source-a"] - * path = source-a - * url = http://localhost:8080/source-a - * branch = . - * - * [submodule "source-b"] - * path = source-b - * url = http://localhost:8080/source-b - * branch = refs/heads/master - * </pre> - * <p> - * It expects to insert new rows in subscriptions table. The rows inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source-a" on branch "refs/heads/master" with "source-a" path</li> - * <li>source "source-b" on branch "refs/heads/master" with "source-b" path</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionsDotAndSameBranchValues() throws Exception { - final StringBuilder sb = - buildSubmoduleSection("source-a", "source-a", - "http://localhost:8080/source-a", "."); - sb.append(buildSubmoduleSection("source-b", "source-b", - "http://localhost:8080/source-b", "refs/heads/master")); - - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> subscriptionsToInsert = new ArrayList<>(); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-a"), "refs/heads/master"), "source-a")); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-b"), "refs/heads/master"), "source-b")); - - doOnlySubscriptionInserts(sb.toString(), mergedBranch, - subscriptionsToInsert); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>no subscriptions existing to destination project</li> - * <li>a commit is merged to "dest-project" in "refs/heads/master" branch</li> - * <li>commit contains .gitmodules file with content</li> - * - * <pre> - * [submodule "source-a"] - * path = source-a - * url = http://localhost:8080/source-a - * branch = refs/heads/test-a - * - * [submodule "source-b"] - * path = source-b - * url = http://localhost:8080/source-b - * branch = refs/heads/test-b - * </pre> - * - * <p> - * It expects to insert new rows in subscriptions table. The rows inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source-a" on branch "refs/heads/test-a" with "source-a" path</li> - * <li>source "source-b" on branch "refs/heads/test-b" with "source-b" path</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testNewSubscriptionsSpecificBranchValues() throws Exception { - final StringBuilder sb = - buildSubmoduleSection("source-a", "source-a", - "http://localhost:8080/source-a", "refs/heads/test-a"); - sb.append(buildSubmoduleSection("source-b", "source-b", - "http://localhost:8080/source-b", "refs/heads/test-b")); - - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> subscriptionsToInsert = new ArrayList<>(); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-a"), "refs/heads/test-a"), "source-a")); - subscriptionsToInsert - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-b"), "refs/heads/test-b"), "source-b")); - - doOnlySubscriptionInserts(sb.toString(), mergedBranch, - subscriptionsToInsert); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>one subscription existing to destination project/branch</li> - * <li>a commit is merged to "dest-project" in "refs/heads/master" branch</li> - * <li>commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "source"] - * path = source - * url = http://localhost:8080/source - * branch = refs/heads/master - * </pre> - * <p> - * It expects to insert a new row in subscriptions table. The rows inserted - * specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "source" on branch "refs/heads/master" with "source" path</li> - * </ul> - * </p> - * <p> - * It also expects to remove the row in subscriptions table specifying another - * project/branch subscribed to merged branch. This one to be removed is: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "old-source" on branch "refs/heads/master" with "old-source" - * path</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testSubscriptionsInsertOneRemoveOne() throws Exception { - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> subscriptionsToInsert = new ArrayList<>(); - subscriptionsToInsert.add(new SubmoduleSubscription(mergedBranch, - new Branch.NameKey(new Project.NameKey("source"), "refs/heads/master"), - "source")); - - List<SubmoduleSubscription> oldOnesToMergedBranch = new ArrayList<>(); - oldOnesToMergedBranch.add(new SubmoduleSubscription(mergedBranch, - new Branch.NameKey(new Project.NameKey("old-source"), - "refs/heads/master"), "old-source")); - - doOnlySubscriptionTableOperations(buildSubmoduleSection("source", "source", - "http://localhost:8080/source", "refs/heads/master").toString(), - mergedBranch, subscriptionsToInsert, oldOnesToMergedBranch); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering: - * <ul> - * <li>one subscription existing to destination project/branch with a source - * called old on refs/heads/master branch</li> - * <li>a commit is merged to "dest-project" in "refs/heads/master" branch</li> - * <li> - * commit contains .gitmodules file with content</li> - * </ul> - * - * <pre> - * [submodule "new"] - * path = new - * url = http://localhost:8080/new - * branch = refs/heads/master - * - * [submodule "old"] - * path = old - * url = http://localhost:8080/old - * branch = refs/heads/master - * </pre> - * <p> - * It expects to insert a new row in subscriptions table. It should not remove - * any row. The rows inserted specifies: - * <ul> - * <li>target "dest-project" on branch "refs/heads/master"</li> - * <li>source "new" on branch "refs/heads/master" with "new" path</li> - * </ul> - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testSubscriptionAddedAndMantainPreviousOne() throws Exception { - final StringBuilder sb = - buildSubmoduleSection("new", "new", "http://localhost:8080/new", - "refs/heads/master"); - sb.append(buildSubmoduleSection("old", "old", "http://localhost:8080/old", - "refs/heads/master")); - - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - final SubmoduleSubscription old = - new SubmoduleSubscription(mergedBranch, new Branch.NameKey(new Project.NameKey( - "old"), "refs/heads/master"), "old"); - - List<SubmoduleSubscription> extractedsubscriptions = new ArrayList<>(); - extractedsubscriptions.add(new SubmoduleSubscription(mergedBranch, - new Branch.NameKey(new Project.NameKey("new"), "refs/heads/master"), - "new")); - extractedsubscriptions.add(old); - - List<SubmoduleSubscription> oldOnesToMergedBranch = new ArrayList<>(); - oldOnesToMergedBranch.add(old); - - doOnlySubscriptionTableOperations(sb.toString(), mergedBranch, - extractedsubscriptions, oldOnesToMergedBranch); - - doVerify(); - } - - /** - * It tests SubmoduleOp.update in a scenario considering an empty .gitmodules - * file is part of a commit to a destination project/branch having two sources - * subscribed. - * <p> - * It expects to remove the subscriptions to destination project/branch. - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testRemoveSubscriptions() throws Exception { - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> extractedsubscriptions = new ArrayList<>(); - List<SubmoduleSubscription> oldOnesToMergedBranch = new ArrayList<>(); - oldOnesToMergedBranch - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-a"), "refs/heads/master"), "source-a")); - oldOnesToMergedBranch - .add(new SubmoduleSubscription(mergedBranch, new Branch.NameKey( - new Project.NameKey("source-b"), "refs/heads/master"), "source-b")); - - doOnlySubscriptionTableOperations("", mergedBranch, extractedsubscriptions, - oldOnesToMergedBranch); - } - - /** - * It tests SubmoduleOp.update in a scenario considering no .gitmodules file - * in a merged commit to a destination project/branch that is a source one to - * one called "target-project". - * <p> - * It expects to update the git link called "source-project" to be in target - * repository. - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testOneSubscriberToUpdate() throws Exception { - expect(schemaFactory.open()).andReturn(schema); - - try (Repository sourceRepository = createWorkRepository(); - Repository targetRepository = createWorkRepository()) { - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git sourceGit = new Git(sourceRepository); - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git targetGit = new Git(targetRepository); - - addRegularFileToIndex("file.txt", "test content", sourceRepository); - - final RevCommit sourceMergeTip = - sourceGit.commit().setMessage("test").call(); - - final Branch.NameKey sourceBranchNameKey = - new Branch.NameKey(new Project.NameKey("source-project"), - "refs/heads/master"); - - final CodeReviewCommit codeReviewCommit = - new CodeReviewCommit(sourceMergeTip.toObjectId()); - final Change submittedChange = new Change( - new Change.Key(sourceMergeTip.toObjectId().getName()), new Change.Id(1), - new Account.Id(1), sourceBranchNameKey, TimeUtil.nowTs()); - - final Map<Change.Id, CodeReviewCommit> mergedCommits = new HashMap<>(); - mergedCommits.put(submittedChange.getId(), codeReviewCommit); - - final List<Change> submitted = new ArrayList<>(); - submitted.add(submittedChange); - - addGitLinkToIndex("a", sourceMergeTip.copy(), targetRepository); - - targetGit.commit().setMessage("test").call(); - - final Branch.NameKey targetBranchNameKey = - new Branch.NameKey(new Project.NameKey("target-project"), - sourceBranchNameKey.get()); - - expect(urlProvider.get()).andReturn("http://localhost:8080"); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - final ResultSet<SubmoduleSubscription> subscribers = - new ListResultSet<>(Collections - .singletonList(new SubmoduleSubscription(targetBranchNameKey, - sourceBranchNameKey, "source-project"))); - expect(subscriptions.bySubmodule(sourceBranchNameKey)).andReturn( - subscribers); - - expect(repoManager.openRepository(targetBranchNameKey.getParentKey())) - .andReturn(targetRepository).anyTimes(); - - Capture<RefUpdate> ruCapture = new Capture<>(); - gitRefUpdated.fire(eq(targetBranchNameKey.getParentKey()), - capture(ruCapture)); - changeHooks.doRefUpdatedHook(eq(targetBranchNameKey), - anyObject(RefUpdate.class), EasyMock.<Account>isNull()); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - final ResultSet<SubmoduleSubscription> emptySubscriptions = - new ListResultSet<>(new ArrayList<SubmoduleSubscription>()); - expect(subscriptions.bySubmodule(targetBranchNameKey)).andReturn( - emptySubscriptions); - - schema.close(); - - final PersonIdent myIdent = - new PersonIdent("test-user", "test-user@email.com"); - - doReplay(); - - final SubmoduleOp submoduleOp = - new SubmoduleOp(sourceBranchNameKey, sourceMergeTip, new RevWalk( - sourceRepository), urlProvider, schemaFactory, sourceRepository, - new Project(sourceBranchNameKey.getParentKey()), submitted, - mergedCommits, myIdent, repoManager, gitRefUpdated, null, - changeHooks); - - submoduleOp.update(); - - doVerify(); - RefUpdate ru = ruCapture.getValue(); - assertEquals(ru.getName(), targetBranchNameKey.get()); - } - } - - /** - * It tests SubmoduleOp.update in a scenario considering established circular - * reference in submodule_subscriptions table. - * <p> - * In the tested scenario there is no .gitmodules file in a merged commit to a - * destination project/branch that is a source one to one called - * "target-project". - * <p> - * submodule_subscriptions table will be incorrect due source appearing as a - * subscriber or target-project: according to database target-project has as - * source the source-project, and source-project has as source the - * target-project. - * <p> - * It expects to update the git link called "source-project" to be in target - * repository and ignoring the incorrect row in database establishing the - * circular reference. - * </p> - * - * @throws Exception If an exception occurs. - */ - @Test - public void testAvoidingCircularReference() throws Exception { - expect(schemaFactory.open()).andReturn(schema); - - try (Repository sourceRepository = createWorkRepository(); - Repository targetRepository = createWorkRepository()) { - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git sourceGit = new Git(sourceRepository); - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git targetGit = new Git(targetRepository); - - addRegularFileToIndex("file.txt", "test content", sourceRepository); - - final RevCommit sourceMergeTip = - sourceGit.commit().setMessage("test").call(); - - final Branch.NameKey sourceBranchNameKey = - new Branch.NameKey(new Project.NameKey("source-project"), - "refs/heads/master"); - - final CodeReviewCommit codeReviewCommit = - new CodeReviewCommit(sourceMergeTip.toObjectId()); - final Change submittedChange = new Change( - new Change.Key(sourceMergeTip.toObjectId().getName()), new Change.Id(1), - new Account.Id(1), sourceBranchNameKey, TimeUtil.nowTs()); - - final Map<Change.Id, CodeReviewCommit> mergedCommits = new HashMap<>(); - mergedCommits.put(submittedChange.getId(), codeReviewCommit); - - final List<Change> submitted = new ArrayList<>(); - submitted.add(submittedChange); - - addGitLinkToIndex("a", sourceMergeTip.copy(), targetRepository); - - targetGit.commit().setMessage("test").call(); - - final Branch.NameKey targetBranchNameKey = - new Branch.NameKey(new Project.NameKey("target-project"), - sourceBranchNameKey.get()); - - expect(urlProvider.get()).andReturn("http://localhost:8080"); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - final ResultSet<SubmoduleSubscription> subscribers = - new ListResultSet<>(Collections - .singletonList(new SubmoduleSubscription(targetBranchNameKey, - sourceBranchNameKey, "source-project"))); - expect(subscriptions.bySubmodule(sourceBranchNameKey)).andReturn( - subscribers); - - expect(repoManager.openRepository(targetBranchNameKey.getParentKey())) - .andReturn(targetRepository).anyTimes(); - - Capture<RefUpdate> ruCapture = new Capture<>(); - gitRefUpdated.fire(eq(targetBranchNameKey.getParentKey()), - capture(ruCapture)); - changeHooks.doRefUpdatedHook(eq(targetBranchNameKey), - anyObject(RefUpdate.class), EasyMock.<Account>isNull()); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - final ResultSet<SubmoduleSubscription> incorrectSubscriptions = - new ListResultSet<>(Collections - .singletonList(new SubmoduleSubscription(sourceBranchNameKey, - targetBranchNameKey, "target-project"))); - expect(subscriptions.bySubmodule(targetBranchNameKey)).andReturn( - incorrectSubscriptions); - - schema.close(); - - final PersonIdent myIdent = - new PersonIdent("test-user", "test-user@email.com"); - - doReplay(); - - final SubmoduleOp submoduleOp = - new SubmoduleOp(sourceBranchNameKey, sourceMergeTip, new RevWalk( - sourceRepository), urlProvider, schemaFactory, sourceRepository, - new Project(sourceBranchNameKey.getParentKey()), submitted, - mergedCommits, myIdent, repoManager, gitRefUpdated, null, changeHooks); - - submoduleOp.update(); - - doVerify(); - RefUpdate ru = ruCapture.getValue(); - assertEquals(ru.getName(), targetBranchNameKey.get()); - } - } - - /** - * It calls SubmoduleOp.update considering only one insert on Subscriptions - * table. - * <p> - * It considers a commit containing a .gitmodules file was merged in - * refs/heads/master of a dest-project. - * </p> - * <p> - * The .gitmodules file content should indicate a source project called - * "source". - * </p> - * - * @param gitModulesFileContent The .gitmodules file content. During the test - * this file is created, so the commit containing it. - * @param sourceBranchName The branch name of source project "pointed by" - * .gitmodules file. - * @throws Exception If an exception occurs. - */ - private void doOneSubscriptionInsert(final String gitModulesFileContent, - final String sourceBranchName) throws Exception { - final Branch.NameKey mergedBranch = - new Branch.NameKey(new Project.NameKey("dest-project"), - "refs/heads/master"); - - List<SubmoduleSubscription> subscriptionsToInsert = new ArrayList<>(); - subscriptionsToInsert.add(new SubmoduleSubscription(mergedBranch, - new Branch.NameKey(new Project.NameKey("source"), sourceBranchName), - "source")); - - doOnlySubscriptionInserts(gitModulesFileContent, mergedBranch, - subscriptionsToInsert); - } - - /** - * It calls SubmoduleOp.update method considering scenario only inserting new - * subscriptions. - * <p> - * In this test a commit is created and considered merged to - * {@code mergedBranch} branch. - * </p> - * <p> - * The destination project the commit was merged is not considered to be a - * source of another project (no subscribers found to this project). - * </p> - * - * @param gitModulesFileContent The .gitmodules file content. - * @param mergedBranch The {@code Branch.NameKey} instance representing the - * project/branch the commit was merged. - * @param extractedSubscriptions The subscription rows extracted from - * gitmodules file. - * @throws Exception If an exception occurs. - */ - private void doOnlySubscriptionInserts(final String gitModulesFileContent, - final Branch.NameKey mergedBranch, - final List<SubmoduleSubscription> extractedSubscriptions) throws Exception { - doOnlySubscriptionTableOperations(gitModulesFileContent, mergedBranch, - extractedSubscriptions, new ArrayList<SubmoduleSubscription>()); - } - - /** - * It calls SubmoduleOp.update method considering scenario only updating - * Subscriptions table. - * <p> - * In this test a commit is created and considered merged to - * {@code mergedBranch} branch. - * </p> - * <p> - * The destination project the commit was merged is not considered to be a - * source of another project (no subscribers found to this project). - * </p> - * - * @param gitModulesFileContent The .gitmodules file content. - * @param mergedBranch The {@code Branch.NameKey} instance representing the - * project/branch the commit was merged. - * @param extractedSubscriptions The subscription rows extracted from - * gitmodules file. - * @param previousSubscriptions The subscription rows to be considering as - * existing and pointing as target to the {@code mergedBranch} - * before updating the table. - * @throws Exception If an exception occurs. - */ - private void doOnlySubscriptionTableOperations( - final String gitModulesFileContent, final Branch.NameKey mergedBranch, - final List<SubmoduleSubscription> extractedSubscriptions, - final List<SubmoduleSubscription> previousSubscriptions) throws Exception { - expect(schemaFactory.open()).andReturn(schema); - - try (Repository realDb = createWorkRepository()) { - // TODO(dborowitz): Use try/finally when this doesn't double-close the repo. - @SuppressWarnings("resource") - final Git git = new Git(realDb); - - addRegularFileToIndex(".gitmodules", gitModulesFileContent, realDb); - - final RevCommit mergeTip = git.commit().setMessage("test").call(); - - expect(urlProvider.get()).andReturn("http://localhost:8080").times(2); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - expect(subscriptions.bySuperProject(mergedBranch)).andReturn( - new ListResultSet<>(previousSubscriptions)); - - SortedSet<Project.NameKey> existingProjects = new TreeSet<>(); - - for (SubmoduleSubscription extracted : extractedSubscriptions) { - existingProjects.add(extracted.getSubmodule().getParentKey()); - } - - for (int index = 0; index < extractedSubscriptions.size(); index++) { - expect(repoManager.list()).andReturn(existingProjects); - } - - final Set<SubmoduleSubscription> alreadySubscribeds = new HashSet<>(); - for (SubmoduleSubscription s : extractedSubscriptions) { - if (previousSubscriptions.contains(s)) { - alreadySubscribeds.add(s); - } - } - - final Set<SubmoduleSubscription> subscriptionsToRemove = - new HashSet<>(previousSubscriptions); - final List<SubmoduleSubscription> subscriptionsToInsert = - new ArrayList<>(extractedSubscriptions); - - subscriptionsToRemove.removeAll(subscriptionsToInsert); - subscriptionsToInsert.removeAll(alreadySubscribeds); - - if (!subscriptionsToRemove.isEmpty()) { - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - subscriptions.delete(subscriptionsToRemove); - } - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - subscriptions.insert(subscriptionsToInsert); - - expect(schema.submoduleSubscriptions()).andReturn(subscriptions); - expect(subscriptions.bySubmodule(mergedBranch)).andReturn( - new ListResultSet<>(new ArrayList<SubmoduleSubscription>())); - - schema.close(); - - doReplay(); - - final SubmoduleOp submoduleOp = - new SubmoduleOp(mergedBranch, mergeTip, new RevWalk(realDb), - urlProvider, schemaFactory, realDb, new Project(mergedBranch - .getParentKey()), new ArrayList<Change>(), null, null, - repoManager, null, null, null); - - submoduleOp.update(); - } - } - - /** - * It creates and adds a regular file to git index of a repository. - * - * @param fileName The file name. - * @param content File content. - * @param repository The Repository instance. - * @throws IOException If an I/O exception occurs. - */ - private void addRegularFileToIndex(final String fileName, - final String content, final Repository repository) throws IOException { - final ObjectInserter oi = repository.newObjectInserter(); - AnyObjectId objectId = - oi.insert(Constants.OBJ_BLOB, Constants.encode(content)); - oi.flush(); - addEntryToIndex(fileName, FileMode.REGULAR_FILE, objectId, repository); - } - - /** - * It creates and adds a git link to git index of a repository. - * - * @param fileName The file name. - * @param objectId The sha-1 value of git link. - * @param repository The Repository instance. - * @throws IOException If an I/O exception occurs. - */ - private void addGitLinkToIndex(final String fileName, - final AnyObjectId objectId, final Repository repository) - throws IOException { - addEntryToIndex(fileName, FileMode.GITLINK, objectId, repository); - } - - /** - * It adds an entry to index. - * - * @param path The entry path. - * @param fileMode The entry file mode. - * @param objectId The ObjectId value of the entry. - * @param repository The repository instance. - * @throws IOException If an I/O exception occurs. - */ - private void addEntryToIndex(final String path, final FileMode fileMode, - final AnyObjectId objectId, final Repository repository) - throws IOException { - final DirCacheEntry e = new DirCacheEntry(path); - e.setFileMode(fileMode); - e.setObjectId(objectId); - - final DirCacheBuilder dirCacheBuilder = repository.lockDirCache().builder(); - dirCacheBuilder.add(e); - dirCacheBuilder.commit(); - } - - private static StringBuilder buildSubmoduleSection(final String name, - final String path, final String url, final String branch) { - final StringBuilder sb = new StringBuilder(); - - sb.append("[submodule \""); - sb.append(name); - sb.append("\"]"); - sb.append(newLine); - - sb.append("\tpath = "); - sb.append(path); - sb.append(newLine); - - sb.append("\turl = "); - sb.append(url); - sb.append(newLine); - - sb.append("\tbranch = "); - sb.append(branch); - sb.append(newLine); - - return sb; - } -}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java index c9a2056..0c8625d 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -27,7 +27,7 @@ FakeQueryBuilder.class), new ChangeQueryBuilder.Arguments(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - indexes, null, null, null, null)); + null, indexes, null, null, null, null)); } @Operator
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java index 02d0582..fa4ac0e 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
@@ -25,7 +25,7 @@ * Holds an in-memory {@link java.io.PrintWriter} object and allows * comparisons of its contents to a supplied string via an assert statement. */ - class PrintWriterComparator { + static class PrintWriterComparator { private PrintWriter printWriter; private StringWriter stringWriter;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java index 1308723..108c20f 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -203,16 +203,16 @@ return label; } - protected PatchLineComment newPublishedPatchLineComment(PatchSet.Id psId, + protected PatchLineComment newPublishedComment(PatchSet.Id psId, String filename, String UUID, CommentRange range, int line, IdentifiedUser commenter, String parentUUID, Timestamp t, String message, short side, String commitSHA1) { - return newPatchLineComment(psId, filename, UUID, range, line, commenter, + return newComment(psId, filename, UUID, range, line, commenter, parentUUID, t, message, side, commitSHA1, PatchLineComment.Status.PUBLISHED); } - protected PatchLineComment newPatchLineComment(PatchSet.Id psId, + protected PatchLineComment newComment(PatchSet.Id psId, String filename, String UUID, CommentRange range, int line, IdentifiedUser commenter, String parentUUID, Timestamp t, String message, short side, String commitSHA1,
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java index aea966a..6067442 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -14,23 +14,19 @@ package com.google.gerrit.server.notedb; +import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.server.notedb.ReviewerState.CC; import static com.google.gerrit.server.notedb.ReviewerState.REVIEWER; import static com.google.gerrit.testutil.TestChanges.incrementPatchSet; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.reviewdb.client.Account; @@ -41,8 +37,8 @@ import com.google.gerrit.reviewdb.client.PatchLineComment.Status; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; +import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate; -import com.google.gwtorm.server.OrmException; import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.CommitBuilder; @@ -50,12 +46,12 @@ import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.notes.Note; +import org.eclipse.jgit.notes.NoteMap; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.ReceiveCommand; import org.junit.Test; -import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.LinkedHashSet; @@ -71,22 +67,23 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(1, notes.getApprovals().keySet().size()); + assertThat(notes.getApprovals().keySet()) + .containsExactly(c.currentPatchSetId()); List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId()); - assertEquals(2, psas.size()); + assertThat(psas).hasSize(2); - assertEquals(c.currentPatchSetId(), psas.get(0).getPatchSetId()); - assertEquals(1, psas.get(0).getAccountId().get()); - assertEquals("Code-Review", psas.get(0).getLabel()); - assertEquals((short) -1, psas.get(0).getValue()); - assertEquals(truncate(after(c, 1000)), psas.get(0).getGranted()); + assertThat(psas.get(0).getPatchSetId()).isEqualTo(c.currentPatchSetId()); + assertThat(psas.get(0).getAccountId().get()).isEqualTo(1); + assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review"); + assertThat(psas.get(0).getValue()).isEqualTo((short) -1); + assertThat(psas.get(0).getGranted()).isEqualTo(truncate(after(c, 1000))); - assertEquals(c.currentPatchSetId(), psas.get(1).getPatchSetId()); - assertEquals(1, psas.get(1).getAccountId().get()); - assertEquals("Verified", psas.get(1).getLabel()); - assertEquals((short) 1, psas.get(1).getValue()); - assertEquals(psas.get(0).getGranted(), psas.get(1).getGranted()); + assertThat(psas.get(1).getPatchSetId()).isEqualTo(c.currentPatchSetId()); + assertThat(psas.get(1).getAccountId().get()).isEqualTo(1); + assertThat(psas.get(1).getLabel()).isEqualTo("Verified"); + assertThat(psas.get(1).getValue()).isEqualTo((short) 1); + assertThat(psas.get(1).getGranted()).isEqualTo(psas.get(0).getGranted()); } @Test @@ -105,21 +102,21 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, PatchSetApproval> psas = notes.getApprovals(); - assertEquals(2, notes.getApprovals().keySet().size()); + assertThat(psas).hasSize(2); PatchSetApproval psa1 = Iterables.getOnlyElement(psas.get(ps1)); - assertEquals(ps1, psa1.getPatchSetId()); - assertEquals(1, psa1.getAccountId().get()); - assertEquals("Code-Review", psa1.getLabel()); - assertEquals((short) -1, psa1.getValue()); - assertEquals(truncate(after(c, 1000)), psa1.getGranted()); + assertThat(psa1.getPatchSetId()).isEqualTo(ps1); + assertThat(psa1.getAccountId().get()).isEqualTo(1); + assertThat(psa1.getLabel()).isEqualTo("Code-Review"); + assertThat(psa1.getValue()).isEqualTo((short) -1); + assertThat(psa1.getGranted()).isEqualTo(truncate(after(c, 1000))); PatchSetApproval psa2 = Iterables.getOnlyElement(psas.get(ps2)); - assertEquals(ps2, psa2.getPatchSetId()); - assertEquals(1, psa2.getAccountId().get()); - assertEquals("Code-Review", psa2.getLabel()); - assertEquals((short) +1, psa2.getValue()); - assertEquals(truncate(after(c, 2000)), psa2.getGranted()); + assertThat(psa2.getPatchSetId()).isEqualTo(ps2); + assertThat(psa2.getAccountId().get()).isEqualTo(1); + assertThat(psa2.getLabel()).isEqualTo("Code-Review"); + assertThat(psa2.getValue()).isEqualTo((short) +1); + assertThat(psa2.getGranted()).isEqualTo(truncate(after(c, 2000))); } @Test @@ -132,8 +129,8 @@ ChangeNotes notes = newNotes(c); PatchSetApproval psa = Iterables.getOnlyElement( notes.getApprovals().get(c.currentPatchSetId())); - assertEquals("Code-Review", psa.getLabel()); - assertEquals((short) -1, psa.getValue()); + assertThat(psa.getLabel()).isEqualTo("Code-Review"); + assertThat(psa.getValue()).isEqualTo((short) -1); update = newUpdate(c, changeOwner); update.putApproval("Code-Review", (short) 1); @@ -142,8 +139,8 @@ notes = newNotes(c); psa = Iterables.getOnlyElement( notes.getApprovals().get(c.currentPatchSetId())); - assertEquals("Code-Review", psa.getLabel()); - assertEquals((short) 1, psa.getValue()); + assertThat(psa.getLabel()).isEqualTo("Code-Review"); + assertThat(psa.getValue()).isEqualTo((short) 1); } @Test @@ -158,22 +155,23 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(1, notes.getApprovals().keySet().size()); + assertThat(notes.getApprovals().keySet()) + .containsExactly(c.currentPatchSetId()); List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId()); - assertEquals(2, psas.size()); + assertThat(psas).hasSize(2); - assertEquals(c.currentPatchSetId(), psas.get(0).getPatchSetId()); - assertEquals(1, psas.get(0).getAccountId().get()); - assertEquals("Code-Review", psas.get(0).getLabel()); - assertEquals((short) -1, psas.get(0).getValue()); - assertEquals(truncate(after(c, 1000)), psas.get(0).getGranted()); + assertThat(psas.get(0).getPatchSetId()).isEqualTo(c.currentPatchSetId()); + assertThat(psas.get(0).getAccountId().get()).isEqualTo(1); + assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review"); + assertThat(psas.get(0).getValue()).isEqualTo((short) -1); + assertThat(psas.get(0).getGranted()).isEqualTo(truncate(after(c, 1000))); - assertEquals(c.currentPatchSetId(), psas.get(1).getPatchSetId()); - assertEquals(2, psas.get(1).getAccountId().get()); - assertEquals("Code-Review", psas.get(1).getLabel()); - assertEquals((short) 1, psas.get(1).getValue()); - assertEquals(truncate(after(c, 2000)), psas.get(1).getGranted()); + assertThat(psas.get(1).getPatchSetId()).isEqualTo(c.currentPatchSetId()); + assertThat(psas.get(1).getAccountId().get()).isEqualTo(2); + assertThat(psas.get(1).getLabel()).isEqualTo("Code-Review"); + assertThat(psas.get(1).getValue()).isEqualTo((short) 1); + assertThat(psas.get(1).getGranted()).isEqualTo(truncate(after(c, 2000))); } @Test @@ -186,16 +184,16 @@ ChangeNotes notes = newNotes(c); PatchSetApproval psa = Iterables.getOnlyElement( notes.getApprovals().get(c.currentPatchSetId())); - assertEquals(1, psa.getAccountId().get()); - assertEquals("Not-For-Long", psa.getLabel()); - assertEquals((short) 1, psa.getValue()); + assertThat(psa.getAccountId().get()).isEqualTo(1); + assertThat(psa.getLabel()).isEqualTo("Not-For-Long"); + assertThat(psa.getValue()).isEqualTo((short) 1); update = newUpdate(c, changeOwner); update.removeApproval("Not-For-Long"); update.commit(); notes = newNotes(c); - assertTrue(notes.getApprovals().isEmpty()); + assertThat(notes.getApprovals()).isEmpty(); } @Test @@ -207,10 +205,10 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(ImmutableSetMultimap.of( + assertThat(notes.getReviewers()).isEqualTo( + ImmutableSetMultimap.of( REVIEWER, new Account.Id(1), - REVIEWER, new Account.Id(2)), - notes.getReviewers()); + REVIEWER, new Account.Id(2))); } @Test @@ -222,10 +220,10 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(ImmutableSetMultimap.of( - REVIEWER, new Account.Id(1), - CC, new Account.Id(2)), - notes.getReviewers()); + assertThat(notes.getReviewers()).isEqualTo( + ImmutableSetMultimap.of( + REVIEWER, new Account.Id(1), + CC, new Account.Id(2))); } @Test @@ -236,18 +234,16 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(ImmutableSetMultimap.of( - REVIEWER, new Account.Id(2)), - notes.getReviewers()); + assertThat(notes.getReviewers()).isEqualTo( + ImmutableSetMultimap.of(REVIEWER, new Account.Id(2))); update = newUpdate(c, otherUser); update.putReviewer(otherUser.getAccount().getId(), CC); update.commit(); notes = newNotes(c); - assertEquals(ImmutableSetMultimap.of( - CC, new Account.Id(2)), - notes.getReviewers()); + assertThat(notes.getReviewers()).isEqualTo( + ImmutableSetMultimap.of(CC, new Account.Id(2))); } @Test @@ -268,9 +264,11 @@ ChangeNotes notes = newNotes(c); List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId()); - assertEquals(2, psas.size()); - assertEquals(changeOwner.getAccount().getId(), psas.get(0).getAccountId()); - assertEquals(otherUser.getAccount().getId(), psas.get(1).getAccountId()); + assertThat(psas).hasSize(2); + assertThat(psas.get(0).getAccountId()) + .isEqualTo(changeOwner.getAccount().getId()); + assertThat(psas.get(1).getAccountId()) + .isEqualTo(otherUser.getAccount().getId()); update = newUpdate(c, changeOwner); update.removeReviewer(otherUser.getAccount().getId()); @@ -278,8 +276,9 @@ notes = newNotes(c); psas = notes.getApprovals().get(c.currentPatchSetId()); - assertEquals(1, psas.size()); - assertEquals(changeOwner.getAccount().getId(), psas.get(0).getAccountId()); + assertThat(psas).hasSize(1); + assertThat(psas.get(0).getAccountId()) + .isEqualTo(changeOwner.getAccount().getId()); } @Test @@ -299,13 +298,15 @@ ChangeNotes notes = newNotes(c); List<SubmitRecord> recs = notes.getSubmitRecords(); - assertEquals(2, recs.size()); - assertEquals(submitRecord("NOT_READY", null, - submitLabel("Verified", "OK", changeOwner.getAccountId()), - submitLabel("Code-Review", "NEED", null)), recs.get(0)); - assertEquals(submitRecord("NOT_READY", null, - submitLabel("Verified", "OK", changeOwner.getAccountId()), - submitLabel("Alternative-Code-Review", "NEED", null)), recs.get(1)); + assertThat(recs).hasSize(2); + assertThat(recs.get(0)).isEqualTo( + submitRecord("NOT_READY", null, + submitLabel("Verified", "OK", changeOwner.getAccountId()), + submitLabel("Code-Review", "NEED", null))); + assertThat(recs.get(1)).isEqualTo( + submitRecord("NOT_READY", null, + submitLabel("Verified", "OK", changeOwner.getAccountId()), + submitLabel("Alternative-Code-Review", "NEED", null))); } @Test @@ -327,16 +328,16 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(submitRecord("OK", null, - submitLabel("Code-Review", "OK", changeOwner.getAccountId())), - Iterables.getOnlyElement(notes.getSubmitRecords())); + assertThat(notes.getSubmitRecords()).containsExactly( + submitRecord("OK", null, + submitLabel("Code-Review", "OK", changeOwner.getAccountId()))); } @Test public void emptyChangeUpdate() throws Exception { ChangeUpdate update = newUpdate(newChange(), changeOwner); update.commit(); - assertNull(update.getRevision()); + assertThat(update.getRevision()).isNull(); } @Test @@ -351,7 +352,7 @@ try (RevWalk walk = new RevWalk(repo)) { RevCommit commit = walk.parseCommit(update.getRevision()); walk.parseBody(commit); - assertTrue(commit.getFullMessage().endsWith("Hashtags: tag1,tag2\n")); + assertThat(commit.getFullMessage()).endsWith("Hashtags: tag1,tag2\n"); } } @@ -366,7 +367,7 @@ update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(hashtags, notes.getHashtags()); + assertThat(notes.getHashtags()).isEqualTo(hashtags); } @Test @@ -374,7 +375,7 @@ ChangeUpdate update = newUpdate(newChange(), changeOwner); update.setSubject("Create change"); update.commit(); - assertNotNull(update.getRevision()); + assertThat(update.getRevision()).isNotNull(); } @Test @@ -398,15 +399,17 @@ ChangeNotes notes = newNotes(c); List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId()); - assertEquals(2, psas.size()); + assertThat(psas).hasSize(2); - assertEquals(changeOwner.getAccount().getId(), psas.get(0).getAccountId()); - assertEquals("Verified", psas.get(0).getLabel()); - assertEquals((short) 1, psas.get(0).getValue()); + assertThat(psas.get(0).getAccountId()) + .isEqualTo(changeOwner.getAccount().getId()); + assertThat(psas.get(0).getLabel()).isEqualTo("Verified"); + assertThat(psas.get(0).getValue()).isEqualTo((short) 1); - assertEquals(otherUser.getAccount().getId(), psas.get(1).getAccountId()); - assertEquals("Code-Review", psas.get(1).getLabel()); - assertEquals((short) 2, psas.get(1).getValue()); + assertThat(psas.get(1).getAccountId()) + .isEqualTo(otherUser.getAccount().getId()); + assertThat(psas.get(1).getLabel()).isEqualTo("Code-Review"); + assertThat(psas.get(1).getValue()).isEqualTo((short) 2); } @Test @@ -420,7 +423,7 @@ PatchSet.Id psId = c.currentPatchSetId(); BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate(); BatchMetaDataUpdate batch = update1.openUpdateInBatch(bru); - PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1", + PatchLineComment comment1 = newPublishedComment(psId, "file1", uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1, (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update1.setPatchSetId(psId); @@ -437,17 +440,17 @@ ChangeNotes notes = newNotes(c); ObjectId tip = notes.getRevision(); RevCommit commitWithApprovals = rw.parseCommit(tip); - assertNotNull(commitWithApprovals); + assertThat(commitWithApprovals).isNotNull(); RevCommit commitWithComments = commitWithApprovals.getParent(0); - assertNotNull(commitWithComments); + assertThat(commitWithComments).isNotNull(); ChangeNotesParser notesWithComments = new ChangeNotesParser(c, commitWithComments.copy(), rw, repoManager); notesWithComments.parseAll(); ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals1 = notesWithComments.buildApprovals(); - assertEquals(0, approvals1.size()); - assertEquals(1, notesWithComments.commentsForBase.size()); + assertThat(approvals1).isEmpty(); + assertThat(notesWithComments.comments).hasSize(1); notesWithComments.close(); ChangeNotesParser notesWithApprovals = @@ -455,8 +458,8 @@ notesWithApprovals.parseAll(); ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals2 = notesWithApprovals.buildApprovals(); - assertEquals(1, approvals2.size()); - assertEquals(1, notesWithApprovals.commentsForBase.size()); + assertThat(approvals2).hasSize(1); + assertThat(notesWithApprovals.comments).hasSize(1); notesWithApprovals.close(); } finally { batch.close(); @@ -481,12 +484,12 @@ batch1 = update1.openUpdateInBatch(bru); batch1.write(update1, new CommitBuilder()); batch1.commit(); - assertNull(repo.getRef(update1.getRefName())); + assertThat(repo.getRef(update1.getRefName())).isNull(); batch2 = update2.openUpdateInBatch(bru); batch2.write(update2, new CommitBuilder()); batch2.commit(); - assertNull(repo.getRef(update2.getRefName())); + assertThat(repo.getRef(update2.getRefName())).isNull(); } finally { if (batch1 != null) { batch1.close(); @@ -497,19 +500,19 @@ } List<ReceiveCommand> cmds = bru.getCommands(); - assertEquals(2, cmds.size()); - assertEquals(update1.getRefName(), cmds.get(0).getRefName()); - assertEquals(update2.getRefName(), cmds.get(1).getRefName()); + assertThat(cmds).hasSize(2); + assertThat(cmds.get(0).getRefName()).isEqualTo(update1.getRefName()); + assertThat(cmds.get(1).getRefName()).isEqualTo(update2.getRefName()); try (RevWalk rw = new RevWalk(repo)) { bru.execute(rw, NullProgressMonitor.INSTANCE); } - assertEquals(ReceiveCommand.Result.OK, cmds.get(0).getResult()); - assertEquals(ReceiveCommand.Result.OK, cmds.get(1).getResult()); + assertThat(cmds.get(0).getResult()).isEqualTo(ReceiveCommand.Result.OK); + assertThat(cmds.get(1).getResult()).isEqualTo(ReceiveCommand.Result.OK); - assertNotNull(repo.getRef(update1.getRefName())); - assertNotNull(repo.getRef(update2.getRefName())); + assertThat(repo.getRef(update1.getRefName())).isNotNull(); + assertThat(repo.getRef(update2.getRefName())).isNotNull(); } @Test @@ -524,14 +527,12 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = notes.getChangeMessages(); - assertEquals(1, changeMessages.keySet().size()); + assertThat(changeMessages.keySet()).containsExactly(ps1); ChangeMessage cm = Iterables.getOnlyElement(changeMessages.get(ps1)); - assertEquals("Just a little code change.\n", - cm.getMessage()); - assertEquals(changeOwner.getAccount().getId(), - cm.getAuthor()); - assertEquals(ps1, cm.getPatchSetId()); + assertThat(cm.getMessage()).isEqualTo("Just a little code change.\n"); + assertThat(cm.getAuthor()).isEqualTo(changeOwner.getAccount().getId()); + assertThat(cm.getPatchSetId()).isEqualTo(ps1); } @Test @@ -542,9 +543,7 @@ update.commit(); ChangeNotes notes = newNotes(c); - ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = - notes.getChangeMessages(); - assertEquals(0, changeMessages.keySet().size()); + assertThat(notes.getChangeMessages()).isEmpty(); } @Test @@ -559,11 +558,11 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = notes.getChangeMessages(); - assertEquals(1, changeMessages.keySet().size()); + assertThat(changeMessages).hasSize(1); ChangeMessage cm1 = Iterables.getOnlyElement(changeMessages.get(ps1)); - assertEquals("Testing trailing double newline\n" + "\n", cm1.getMessage()); - assertEquals(changeOwner.getAccount().getId(), cm1.getAuthor()); + assertThat(cm1.getMessage()).isEqualTo("Testing trailing double newline\n" + "\n"); + assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId()); } @Test @@ -581,15 +580,15 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = notes.getChangeMessages(); - assertEquals(1, changeMessages.keySet().size()); + assertThat(changeMessages).hasSize(1); ChangeMessage cm1 = Iterables.getOnlyElement(changeMessages.get(ps1)); - assertEquals("Testing paragraph 1\n" + assertThat(cm1.getMessage()).isEqualTo("Testing paragraph 1\n" + "\n" + "Testing paragraph 2\n" + "\n" - + "Testing paragraph 3", cm1.getMessage()); - assertEquals(changeOwner.getAccount().getId(), cm1.getAuthor()); + + "Testing paragraph 3"); + assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId()); } @Test @@ -611,20 +610,19 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = notes.getChangeMessages(); - assertEquals(2, changeMessages.keySet().size()); + assertThat(changeMessages).hasSize(2); ChangeMessage cm1 = Iterables.getOnlyElement(changeMessages.get(ps1)); - assertEquals("This is the change message for the first PS.", - cm1.getMessage()); - assertEquals(changeOwner.getAccount().getId(), - cm1.getAuthor()); + assertThat(cm1.getMessage()) + .isEqualTo("This is the change message for the first PS."); + assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId()); ChangeMessage cm2 = Iterables.getOnlyElement(changeMessages.get(ps2)); - assertEquals(ps1, cm1.getPatchSetId()); - assertEquals("This is the change message for the second PS.", - cm2.getMessage()); - assertEquals(changeOwner.getAccount().getId(), cm2.getAuthor()); - assertEquals(ps2, cm2.getPatchSetId()); + assertThat(cm1.getPatchSetId()).isEqualTo(ps1); + assertThat(cm2.getMessage()) + .isEqualTo("This is the change message for the second PS."); + assertThat(cm2.getAuthor()).isEqualTo(changeOwner.getAccount().getId()); + assertThat(cm2.getPatchSetId()).isEqualTo(ps2); } @Test @@ -645,20 +643,18 @@ ChangeNotes notes = newNotes(c); ListMultimap<PatchSet.Id, ChangeMessage> changeMessages = notes.getChangeMessages(); - assertEquals(1, changeMessages.keySet().size()); + assertThat(changeMessages.keySet()).hasSize(1); List<ChangeMessage> cm = changeMessages.get(ps1); - assertEquals(2, cm.size()); - assertEquals("First change message.\n", - cm.get(0).getMessage()); - assertEquals(changeOwner.getAccount().getId(), - cm.get(0).getAuthor()); - assertEquals(ps1, cm.get(0).getPatchSetId()); - assertEquals("Second change message.\n", - cm.get(1).getMessage()); - assertEquals(changeOwner.getAccount().getId(), - cm.get(1).getAuthor()); - assertEquals(ps1, cm.get(1).getPatchSetId()); + assertThat(cm).hasSize(2); + assertThat(cm.get(0).getMessage()).isEqualTo("First change message.\n"); + assertThat(cm.get(0).getAuthor()) + .isEqualTo(changeOwner.getAccount().getId()); + assertThat(cm.get(0).getPatchSetId()).isEqualTo(ps1); + assertThat(cm.get(1).getMessage()).isEqualTo("Second change message.\n"); + assertThat(cm.get(1).getAuthor()) + .isEqualTo(changeOwner.getAccount().getId()); + assertThat(cm.get(1).getPatchSetId()).isEqualTo(ps1); } @Test @@ -677,7 +673,7 @@ Timestamp time3 = TimeUtil.nowTs(); PatchSet.Id psId = c.currentPatchSetId(); - PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1", + PatchLineComment comment1 = newPublishedComment(psId, "file1", uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1, (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update.setPatchSetId(psId); @@ -686,7 +682,7 @@ update = newUpdate(c, otherUser); CommentRange range2 = new CommentRange(2, 1, 3, 1); - PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1", + PatchLineComment comment2 = newPublishedComment(psId, "file1", uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2, (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update.setPatchSetId(psId); @@ -695,7 +691,7 @@ update = newUpdate(c, otherUser); CommentRange range3 = new CommentRange(3, 1, 4, 1); - PatchLineComment comment3 = newPublishedPatchLineComment(psId, "file2", + PatchLineComment comment3 = newPublishedComment(psId, "file2", uuid3, range3, range3.getEndLine(), otherUser, null, time3, message3, (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update.setPatchSetId(psId); @@ -713,7 +709,7 @@ walk.getObjectReader().open( note.getData(), Constants.OBJ_BLOB).getBytes(); String noteString = new String(bytes, UTF_8); - assertEquals("Patch-set: 1\n" + assertThat(noteString).isEqualTo("Patch-set: 1\n" + "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n" + "File: file1\n" + "\n" @@ -739,8 +735,7 @@ + "UUID: uuid3\n" + "Bytes: 9\n" + "comment 3\n" - + "\n", - noteString); + + "\n"); } } @@ -757,7 +752,7 @@ Timestamp time2 = TimeUtil.nowTs(); PatchSet.Id psId = c.currentPatchSetId(); - PatchLineComment comment1 = newPublishedPatchLineComment(psId, "file1", + PatchLineComment comment1 = newPublishedComment(psId, "file1", uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1, (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update.setPatchSetId(psId); @@ -766,7 +761,7 @@ update = newUpdate(c, otherUser); CommentRange range2 = new CommentRange(2, 1, 3, 1); - PatchLineComment comment2 = newPublishedPatchLineComment(psId, "file1", + PatchLineComment comment2 = newPublishedComment(psId, "file1", uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2, (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); update.setPatchSetId(psId); @@ -784,7 +779,7 @@ walk.getObjectReader().open( note.getData(), Constants.OBJ_BLOB).getBytes(); String noteString = new String(bytes, UTF_8); - assertEquals("Base-for-patch-set: 1\n" + assertThat(noteString).isEqualTo("Base-for-patch-set: 1\n" + "Revision: abcd1234abcd1234abcd1234abcd1234abcd1234\n" + "File: file1\n" + "\n" @@ -801,8 +796,7 @@ + "UUID: uuid2\n" + "Bytes: 9\n" + "comment 2\n" - + "\n", - noteString); + + "\n"); } } @@ -813,6 +807,8 @@ ChangeUpdate update = newUpdate(c, otherUser); String uuid1 = "uuid1"; String uuid2 = "uuid2"; + String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; String messageForBase = "comment for base"; String messageForPS = "comment for ps"; CommentRange range = new CommentRange(1, 1, 2, 1); @@ -820,34 +816,26 @@ PatchSet.Id psId = c.currentPatchSetId(); PatchLineComment commentForBase = - newPublishedPatchLineComment(psId, "filename", uuid1, + newPublishedComment(psId, "filename", uuid1, range, range.getEndLine(), otherUser, null, now, messageForBase, - (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + (short) 0, rev1); update.setPatchSetId(psId); update.upsertComment(commentForBase); update.commit(); update = newUpdate(c, otherUser); PatchLineComment commentForPS = - newPublishedPatchLineComment(psId, "filename", uuid2, + newPublishedComment(psId, "filename", uuid2, range, range.getEndLine(), otherUser, null, now, messageForPS, - (short) 1, "abcd4567abcd4567abcd4567abcd4567abcd4567"); + (short) 1, rev2); update.setPatchSetId(psId); update.upsertComment(commentForPS); update.commit(); - ChangeNotes notes = newNotes(c); - Multimap<PatchSet.Id, PatchLineComment> commentsForBase = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> commentsForPS = - notes.getPatchSetComments(); - assertEquals(commentsForBase.size(), 1); - assertEquals(commentsForPS.size(), 1); - - assertEquals(commentForBase, - Iterables.getOnlyElement(commentsForBase.get(psId))); - assertEquals(commentForPS, - Iterables.getOnlyElement(commentsForPS.get(psId))); + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of( + new RevId(rev1), commentForBase, + new RevId(rev2), commentForPS)); } @Test @@ -855,6 +843,7 @@ Change c = newChange(); String uuid1 = "uuid1"; String uuid2 = "uuid2"; + String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234"; CommentRange range = new CommentRange(1, 1, 2, 1); PatchSet.Id psId = c.currentPatchSetId(); String filename = "filename"; @@ -863,37 +852,25 @@ ChangeUpdate update = newUpdate(c, otherUser); Timestamp timeForComment1 = TimeUtil.nowTs(); Timestamp timeForComment2 = TimeUtil.nowTs(); - PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename, + PatchLineComment comment1 = newPublishedComment(psId, filename, uuid1, range, range.getEndLine(), otherUser, null, timeForComment1, - "comment 1", side, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + "comment 1", side, rev); update.setPatchSetId(psId); update.upsertComment(comment1); update.commit(); update = newUpdate(c, otherUser); - PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename, + PatchLineComment comment2 = newPublishedComment(psId, filename, uuid2, range, range.getEndLine(), otherUser, null, timeForComment2, - "comment 2", side, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + "comment 2", side, rev); update.setPatchSetId(psId); update.upsertComment(comment2); update.commit(); - ChangeNotes notes = newNotes(c); - Multimap<PatchSet.Id, PatchLineComment> commentsForBase = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> commentsForPS = - notes.getPatchSetComments(); - assertEquals(commentsForBase.size(), 0); - assertEquals(commentsForPS.size(), 2); - - ImmutableList<PatchLineComment> commentsForThisPS = - (ImmutableList<PatchLineComment>) commentsForPS.get(psId); - assertEquals(commentsForThisPS.size(), 2); - PatchLineComment commentFromNotes1 = commentsForThisPS.get(0); - PatchLineComment commentFromNotes2 = commentsForThisPS.get(1); - - assertEquals(comment1, commentFromNotes1); - assertEquals(comment2, commentFromNotes2); + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of( + new RevId(rev), comment1, + new RevId(rev), comment2)).inOrder(); } @Test @@ -901,6 +878,7 @@ throws Exception { Change c = newChange(); String uuid = "uuid"; + String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234"; CommentRange range = new CommentRange(1, 1, 2, 1); PatchSet.Id psId = c.currentPatchSetId(); String filename1 = "filename1"; @@ -909,43 +887,33 @@ ChangeUpdate update = newUpdate(c, otherUser); Timestamp now = TimeUtil.nowTs(); - PatchLineComment comment1 = newPublishedPatchLineComment(psId, filename1, + PatchLineComment comment1 = newPublishedComment(psId, filename1, uuid, range, range.getEndLine(), otherUser, null, now, "comment 1", - side, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + side, rev); update.setPatchSetId(psId); update.upsertComment(comment1); update.commit(); update = newUpdate(c, otherUser); - PatchLineComment comment2 = newPublishedPatchLineComment(psId, filename2, + PatchLineComment comment2 = newPublishedComment(psId, filename2, uuid, range, range.getEndLine(), otherUser, null, now, "comment 2", - side, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + side, rev); update.setPatchSetId(psId); update.upsertComment(comment2); update.commit(); - ChangeNotes notes = newNotes(c); - Multimap<PatchSet.Id, PatchLineComment> commentsForBase = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> commentsForPS = - notes.getPatchSetComments(); - assertEquals(commentsForBase.size(), 0); - assertEquals(commentsForPS.size(), 2); - - ImmutableList<PatchLineComment> commentsForThisPS = - (ImmutableList<PatchLineComment>) commentsForPS.get(psId); - assertEquals(commentsForThisPS.size(), 2); - PatchLineComment commentFromNotes1 = commentsForThisPS.get(0); - PatchLineComment commentFromNotes2 = commentsForThisPS.get(1); - - assertEquals(comment1, commentFromNotes1); - assertEquals(comment2, commentFromNotes2); + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of( + new RevId(rev), comment1, + new RevId(rev), comment2)).inOrder(); } @Test public void patchLineCommentMultiplePatchsets() throws Exception { Change c = newChange(); String uuid = "uuid"; + String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; CommentRange range = new CommentRange(1, 1, 2, 1); PatchSet.Id ps1 = c.currentPatchSetId(); String filename = "filename1"; @@ -953,9 +921,9 @@ ChangeUpdate update = newUpdate(c, otherUser); Timestamp now = TimeUtil.nowTs(); - PatchLineComment comment1 = newPublishedPatchLineComment(ps1, filename, + PatchLineComment comment1 = newPublishedComment(ps1, filename, uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1", - side, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + side, rev1); update.setPatchSetId(ps1); update.upsertComment(comment1); update.commit(); @@ -965,37 +933,24 @@ update = newUpdate(c, otherUser); now = TimeUtil.nowTs(); - PatchLineComment comment2 = newPublishedPatchLineComment(ps2, filename, + PatchLineComment comment2 = newPublishedComment(ps2, filename, uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2", - side, "abcd4567abcd4567abcd4567abcd4567abcd4567"); + side, rev2); update.setPatchSetId(ps2); update.upsertComment(comment2); update.commit(); - ChangeNotes notes = newNotes(c); - LinkedListMultimap<PatchSet.Id, PatchLineComment> commentsForBase = - LinkedListMultimap.create(notes.getBaseComments()); - LinkedListMultimap<PatchSet.Id, PatchLineComment> commentsForPS = - LinkedListMultimap.create(notes.getPatchSetComments()); - assertEquals(commentsForBase.keys().size(), 0); - assertEquals(commentsForPS.values().size(), 2); - - List<PatchLineComment> commentsForPS1 = commentsForPS.get(ps1); - assertEquals(commentsForPS1.size(), 1); - PatchLineComment commentFromPs1 = commentsForPS1.get(0); - - List<PatchLineComment> commentsForPS2 = commentsForPS.get(ps2); - assertEquals(commentsForPS2.size(), 1); - PatchLineComment commentFromPs2 = commentsForPS2.get(0); - - assertEquals(comment1, commentFromPs1); - assertEquals(comment2, commentFromPs2); + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of( + new RevId(rev1), comment1, + new RevId(rev2), comment2)); } @Test public void patchLineCommentSingleDraftToPublished() throws Exception { Change c = newChange(); String uuid = "uuid"; + String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567"; CommentRange range = new CommentRange(1, 1, 2, 1); PatchSet.Id ps1 = c.currentPatchSetId(); String filename = "filename1"; @@ -1003,16 +958,17 @@ ChangeUpdate update = newUpdate(c, otherUser); Timestamp now = TimeUtil.nowTs(); - PatchLineComment comment1 = newPatchLineComment(ps1, filename, uuid, - range, range.getEndLine(), otherUser, null, now, "comment on ps1", side, - "abcd4567abcd4567abcd4567abcd4567abcd4567", Status.DRAFT); + PatchLineComment comment1 = newComment(ps1, filename, uuid, range, + range.getEndLine(), otherUser, null, now, "comment on ps1", side, + rev, Status.DRAFT); update.setPatchSetId(ps1); update.insertComment(comment1); update.commit(); ChangeNotes notes = newNotes(c); - assertEquals(1, notes.getDraftPsComments(otherUserId).values().size()); - assertEquals(0, notes.getDraftBaseComments(otherUserId).values().size()); + assertThat(notes.getDraftComments(otherUserId)).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment1)); + assertThat(notes.getComments()).isEmpty(); comment1.setStatus(Status.PUBLISHED); update = newUpdate(c, otherUser); @@ -1021,49 +977,44 @@ update.commit(); notes = newNotes(c); - - assertTrue(notes.getDraftPsComments(otherUserId).values().isEmpty()); - assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty()); - - assertTrue(notes.getBaseComments().values().isEmpty()); - PatchLineComment commentFromNotes = - Iterables.getOnlyElement(notes.getPatchSetComments().values()); - assertEquals(comment1, commentFromNotes); + assertThat(notes.getDraftComments(otherUserId)).isEmpty(); + assertThat(notes.getComments()).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment1)); } @Test public void patchLineCommentMultipleDraftsSameSidePublishOne() - throws OrmException, IOException { + throws Exception { Change c = newChange(); String uuid1 = "uuid1"; String uuid2 = "uuid2"; + String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567"; CommentRange range1 = new CommentRange(1, 1, 2, 2); CommentRange range2 = new CommentRange(2, 2, 3, 3); String filename = "filename1"; short side = (short) 1; Timestamp now = TimeUtil.nowTs(); - String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; PatchSet.Id psId = c.currentPatchSetId(); // Write two drafts on the same side of one patch set. ChangeUpdate update = newUpdate(c, otherUser); update.setPatchSetId(psId); - PatchLineComment comment1 = newPatchLineComment(psId, filename, uuid1, + PatchLineComment comment1 = newComment(psId, filename, uuid1, range1, range1.getEndLine(), otherUser, null, now, "comment on ps1", - side, commitSHA1, Status.DRAFT); - PatchLineComment comment2 = newPatchLineComment(psId, filename, uuid2, + side, rev, Status.DRAFT); + PatchLineComment comment2 = newComment(psId, filename, uuid2, range2, range2.getEndLine(), otherUser, null, now, "other on ps1", - side, commitSHA1, Status.DRAFT); + side, rev, Status.DRAFT); update.insertComment(comment1); update.insertComment(comment2); update.commit(); ChangeNotes notes = newNotes(c); - assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty()); - assertEquals(2, notes.getDraftPsComments(otherUserId).values().size()); - - assertTrue(notes.getDraftPsComments(otherUserId).containsValue(comment1)); - assertTrue(notes.getDraftPsComments(otherUserId).containsValue(comment2)); + assertThat(notes.getDraftComments(otherUserId)).containsExactly( + ImmutableMultimap.of( + new RevId(rev), comment1, + new RevId(rev), comment2)).inOrder(); + assertThat(notes.getComments()).isEmpty(); // Publish first draft. update = newUpdate(c, otherUser); @@ -1073,54 +1024,46 @@ update.commit(); notes = newNotes(c); - assertEquals(comment1, - Iterables.getOnlyElement(notes.getPatchSetComments().get(psId))); - assertEquals(comment2, - Iterables.getOnlyElement( - notes.getDraftPsComments(otherUserId).values())); - - assertTrue(notes.getBaseComments().values().isEmpty()); - assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty()); + assertThat(notes.getDraftComments(otherUserId)).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment2)); + assertThat(notes.getComments()).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment1)); } @Test public void patchLineCommentsMultipleDraftsBothSidesPublishAll() - throws OrmException, IOException { + throws Exception { Change c = newChange(); String uuid1 = "uuid1"; String uuid2 = "uuid2"; + String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; CommentRange range1 = new CommentRange(1, 1, 2, 2); CommentRange range2 = new CommentRange(2, 2, 3, 3); String filename = "filename1"; Timestamp now = TimeUtil.nowTs(); - String commitSHA1 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; - String baseSHA1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; PatchSet.Id psId = c.currentPatchSetId(); // Write two drafts, one on each side of the patchset. ChangeUpdate update = newUpdate(c, otherUser); update.setPatchSetId(psId); - PatchLineComment baseComment = newPatchLineComment(psId, filename, uuid1, + PatchLineComment baseComment = newComment(psId, filename, uuid1, range1, range1.getEndLine(), otherUser, null, now, "comment on base", - (short) 0, baseSHA1, Status.DRAFT); - PatchLineComment psComment = newPatchLineComment(psId, filename, uuid2, + (short) 0, rev1, Status.DRAFT); + PatchLineComment psComment = newComment(psId, filename, uuid2, range2, range2.getEndLine(), otherUser, null, now, "comment on ps", - (short) 1, commitSHA1, Status.DRAFT); + (short) 1, rev2, Status.DRAFT); update.insertComment(baseComment); update.insertComment(psComment); update.commit(); ChangeNotes notes = newNotes(c); - PatchLineComment baseDraftCommentFromNotes = - Iterables.getOnlyElement( - notes.getDraftBaseComments(otherUserId).values()); - PatchLineComment psDraftCommentFromNotes = - Iterables.getOnlyElement( - notes.getDraftPsComments(otherUserId).values()); - - assertEquals(baseComment, baseDraftCommentFromNotes); - assertEquals(psComment, psDraftCommentFromNotes); + assertThat(notes.getDraftComments(otherUserId)).containsExactly( + ImmutableMultimap.of( + new RevId(rev1), baseComment, + new RevId(rev2), psComment)); + assertThat(notes.getComments()).isEmpty(); // Publish both comments. update = newUpdate(c, otherUser); @@ -1133,17 +1076,98 @@ update.commit(); notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).isEmpty(); + assertThat(notes.getComments()).containsExactly( + ImmutableMultimap.of( + new RevId(rev1), baseComment, + new RevId(rev2), psComment)); + } - PatchLineComment baseCommentFromNotes = - Iterables.getOnlyElement(notes.getBaseComments().values()); - PatchLineComment psCommentFromNotes = - Iterables.getOnlyElement(notes.getPatchSetComments().values()); + @Test + public void patchLineCommentsDeleteAllDrafts() throws Exception { + Change c = newChange(); + String uuid = "uuid"; + String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + ObjectId objId = ObjectId.fromString(rev); + CommentRange range = new CommentRange(1, 1, 2, 1); + PatchSet.Id psId = c.currentPatchSetId(); + String filename = "filename"; + short side = (short) 1; - assertEquals(baseComment, baseCommentFromNotes); - assertEquals(psComment, psCommentFromNotes); + ChangeUpdate update = newUpdate(c, otherUser); + Timestamp now = TimeUtil.nowTs(); + PatchLineComment comment = newComment(psId, filename, uuid, range, + range.getEndLine(), otherUser, null, now, "comment on ps1", side, + rev, Status.DRAFT); + update.setPatchSetId(psId); + update.upsertComment(comment); + update.commit(); - assertTrue(notes.getDraftBaseComments(otherUserId).values().isEmpty()); - assertTrue(notes.getDraftPsComments(otherUserId).values().isEmpty()); + ChangeNotes notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).hasSize(1); + assertThat(notes.getDraftCommentNotes().getNoteMap().contains(objId)) + .isTrue(); + + update = newUpdate(c, otherUser); + now = TimeUtil.nowTs(); + update.setPatchSetId(psId); + update.deleteComment(comment); + update.commit(); + + notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).isEmpty(); + assertThat(notes.getDraftCommentNotes().getNoteMap()).isNull(); + } + + @Test + public void patchLineCommentsDeleteAllDraftsForOneRevision() + throws Exception { + Change c = newChange(); + String uuid = "uuid"; + String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; + ObjectId objId1 = ObjectId.fromString(rev1); + ObjectId objId2 = ObjectId.fromString(rev2); + CommentRange range = new CommentRange(1, 1, 2, 1); + PatchSet.Id ps1 = c.currentPatchSetId(); + String filename = "filename1"; + short side = (short) 1; + + ChangeUpdate update = newUpdate(c, otherUser); + Timestamp now = TimeUtil.nowTs(); + PatchLineComment comment1 = newComment(ps1, filename, + uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1", + side, rev1, Status.DRAFT); + update.setPatchSetId(ps1); + update.upsertComment(comment1); + update.commit(); + + incrementPatchSet(c); + PatchSet.Id ps2 = c.currentPatchSetId(); + + update = newUpdate(c, otherUser); + now = TimeUtil.nowTs(); + PatchLineComment comment2 = newComment(ps2, filename, + uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2", + side, rev2, Status.DRAFT); + update.setPatchSetId(ps2); + update.upsertComment(comment2); + update.commit(); + + ChangeNotes notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).hasSize(2); + + update = newUpdate(c, otherUser); + now = TimeUtil.nowTs(); + update.setPatchSetId(ps2); + update.deleteComment(comment2); + update.commit(); + + notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).hasSize(1); + NoteMap noteMap = notes.getDraftCommentNotes().getNoteMap(); + assertThat(noteMap.contains(objId1)).isTrue(); + assertThat(noteMap.contains(objId2)).isFalse(); } @Test @@ -1151,27 +1175,20 @@ Change c = newChange(); ChangeUpdate update = newUpdate(c, otherUser); String uuid = "uuid"; + String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234"; String messageForBase = "comment for base"; Timestamp now = TimeUtil.nowTs(); PatchSet.Id psId = c.currentPatchSetId(); - PatchLineComment commentForBase = - newPublishedPatchLineComment(psId, "filename", uuid, - null, 0, otherUser, null, now, messageForBase, - (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + PatchLineComment comment = newPublishedComment( + psId, "filename", uuid, null, 0, otherUser, null, now, messageForBase, + (short) 0, rev); update.setPatchSetId(psId); - update.upsertComment(commentForBase); + update.upsertComment(comment); update.commit(); - ChangeNotes notes = newNotes(c); - Multimap<PatchSet.Id, PatchLineComment> commentsForBase = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> commentsForPs = - notes.getPatchSetComments(); - - assertTrue(commentsForPs.isEmpty()); - assertEquals(commentForBase, - Iterables.getOnlyElement(commentsForBase.get(psId))); + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment)); } @Test @@ -1179,26 +1196,63 @@ Change c = newChange(); ChangeUpdate update = newUpdate(c, otherUser); String uuid = "uuid"; + String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234"; String messageForBase = "comment for base"; Timestamp now = TimeUtil.nowTs(); PatchSet.Id psId = c.currentPatchSetId(); - PatchLineComment commentForBase = - newPublishedPatchLineComment(psId, "filename", uuid, - null, 1, otherUser, null, now, messageForBase, - (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234"); + PatchLineComment comment = newPublishedComment( + psId, "filename", uuid, null, 1, otherUser, null, now, messageForBase, + (short) 0, rev); update.setPatchSetId(psId); - update.upsertComment(commentForBase); + update.upsertComment(comment); + update.commit(); + + assertThat(newNotes(c).getComments()).containsExactly( + ImmutableMultimap.of(new RevId(rev), comment)); + } + + @Test + public void updateCommentsForMultipleRevisions() throws Exception { + Change c = newChange(); + String uuid = "uuid"; + String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234"; + String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567"; + CommentRange range = new CommentRange(1, 1, 2, 1); + PatchSet.Id ps1 = c.currentPatchSetId(); + String filename = "filename1"; + short side = (short) 1; + + incrementPatchSet(c); + PatchSet.Id ps2 = c.currentPatchSetId(); + + ChangeUpdate update = newUpdate(c, otherUser); + update.setPatchSetId(ps2); + Timestamp now = TimeUtil.nowTs(); + PatchLineComment comment1 = newComment(ps1, filename, + uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1", + side, rev1, Status.DRAFT); + PatchLineComment comment2 = newComment(ps2, filename, + uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2", + side, rev2, Status.DRAFT); + update.upsertComment(comment1); + update.upsertComment(comment2); update.commit(); ChangeNotes notes = newNotes(c); - Multimap<PatchSet.Id, PatchLineComment> commentsForBase = - notes.getBaseComments(); - Multimap<PatchSet.Id, PatchLineComment> commentsForPs = - notes.getPatchSetComments(); + assertThat(notes.getDraftComments(otherUserId)).hasSize(2); + assertThat(notes.getComments()).isEmpty(); - assertTrue(commentsForPs.isEmpty()); - assertEquals(commentForBase, - Iterables.getOnlyElement(commentsForBase.get(psId))); + update = newUpdate(c, otherUser); + update.setPatchSetId(ps2); + comment1.setStatus(Status.PUBLISHED); + comment2.setStatus(Status.PUBLISHED); + update.upsertComment(comment1); + update.upsertComment(comment2); + update.commit(); + + notes = newNotes(c); + assertThat(notes.getDraftComments(otherUserId)).isEmpty(); + assertThat(notes.getComments()).hasSize(2); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/CommitMessageOutputTest.java index 328509a..49b61cc 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/CommitMessageOutputTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -14,9 +14,9 @@ package com.google.gerrit.server.notedb; +import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.server.notedb.ReviewerState.CC; import static com.google.gerrit.server.notedb.ReviewerState.REVIEWER; -import static org.junit.Assert.assertEquals; import com.google.common.collect.ImmutableList; import com.google.gerrit.common.TimeUtil; @@ -43,7 +43,7 @@ update.putReviewer(changeOwner.getAccount().getId(), REVIEWER); update.putReviewer(otherUser.getAccount().getId(), CC); update.commit(); - assertEquals("refs/changes/01/1/meta", update.getRefName()); + assertThat(update.getRefName()).isEqualTo("refs/changes/01/1/meta"); RevCommit commit = parseCommit(update.getRevision()); assertBodyEquals("Update patch set 1\n" @@ -56,17 +56,18 @@ commit); PersonIdent author = commit.getAuthorIdent(); - assertEquals("Change Owner", author.getName()); - assertEquals("1@gerrit", author.getEmailAddress()); - assertEquals(new Date(c.getCreatedOn().getTime() + 1000), - author.getWhen()); - assertEquals(TimeZone.getTimeZone("GMT-7:00"), author.getTimeZone()); + assertThat(author.getName()).isEqualTo("Change Owner"); + assertThat(author.getEmailAddress()).isEqualTo("1@gerrit"); + assertThat(author.getWhen()) + .isEqualTo(new Date(c.getCreatedOn().getTime() + 1000)); + assertThat(author.getTimeZone()) + .isEqualTo(TimeZone.getTimeZone("GMT-7:00")); PersonIdent committer = commit.getCommitterIdent(); - assertEquals("Gerrit Server", committer.getName()); - assertEquals("noreply@gerrit.com", committer.getEmailAddress()); - assertEquals(author.getWhen(), committer.getWhen()); - assertEquals(author.getTimeZone(), committer.getTimeZone()); + assertThat(committer.getName()).isEqualTo("Gerrit Server"); + assertThat(committer.getEmailAddress()).isEqualTo("noreply@gerrit.com"); + assertThat(committer.getWhen()).isEqualTo(author.getWhen()); + assertThat(committer.getTimeZone()).isEqualTo(author.getTimeZone()); } @Test @@ -76,7 +77,7 @@ update.setChangeMessage("Just a little code change.\n" + "How about a new line"); update.commit(); - assertEquals("refs/changes/01/1/meta", update.getRefName()); + assertThat(update.getRefName()).isEqualTo("refs/changes/01/1/meta"); assertBodyEquals("Update patch set 1\n" + "\n" @@ -130,17 +131,18 @@ commit); PersonIdent author = commit.getAuthorIdent(); - assertEquals("Change Owner", author.getName()); - assertEquals("1@gerrit", author.getEmailAddress()); - assertEquals(new Date(c.getCreatedOn().getTime() + 1000), - author.getWhen()); - assertEquals(TimeZone.getTimeZone("GMT-7:00"), author.getTimeZone()); + assertThat(author.getName()).isEqualTo("Change Owner"); + assertThat(author.getEmailAddress()).isEqualTo("1@gerrit"); + assertThat(author.getWhen()) + .isEqualTo(new Date(c.getCreatedOn().getTime() + 1000)); + assertThat(author.getTimeZone()) + .isEqualTo(TimeZone.getTimeZone("GMT-7:00")); PersonIdent committer = commit.getCommitterIdent(); - assertEquals("Gerrit Server", committer.getName()); - assertEquals("noreply@gerrit.com", committer.getEmailAddress()); - assertEquals(author.getWhen(), committer.getWhen()); - assertEquals(author.getTimeZone(), committer.getTimeZone()); + assertThat(committer.getName()).isEqualTo("Gerrit Server"); + assertThat(committer.getEmailAddress()).isEqualTo("noreply@gerrit.com"); + assertThat(committer.getWhen()).isEqualTo(author.getWhen()); + assertThat(committer.getTimeZone()).isEqualTo(author.getTimeZone()); } @Test @@ -161,8 +163,8 @@ commit); PersonIdent author = commit.getAuthorIdent(); - assertEquals("Anonymous Coward (3)", author.getName()); - assertEquals("3@gerrit", author.getEmailAddress()); + assertThat(author.getName()).isEqualTo("Anonymous Coward (3)"); + assertThat(author.getEmailAddress()).isEqualTo("3@gerrit"); } @Test @@ -252,6 +254,6 @@ private void assertBodyEquals(String expected, ObjectId commitId) throws Exception { RevCommit commit = parseCommit(commitId); - assertEquals(expected, commit.getFullMessage()); + assertThat(commit.getFullMessage()).isEqualTo(expected); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java index b5b321e..0bd4f51 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java
@@ -36,6 +36,7 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.google.inject.util.Providers; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; @@ -49,7 +50,7 @@ /** Unit tests for {@link ProjectControl}. */ public class ProjectControlTest { @Inject private AccountManager accountManager; - @Inject private IdentifiedUser.RequestFactory userFactory; + @Inject private IdentifiedUser.GenericFactory userFactory; @Inject private InMemoryDatabase schemaFactory; @Inject private InMemoryRepositoryManager repoManager; @Inject private ProjectControl.GenericFactory projectControlFactory; @@ -73,7 +74,7 @@ schemaCreator.create(db); Account.Id userId = accountManager.authenticate(AuthRequest.forUser("user")) .getAccountId(); - user = userFactory.create(userId); + user = userFactory.create(Providers.of(db), userId); Project.NameKey name = new Project.NameKey("project"); InMemoryRepository inMemoryRepo = repoManager.createRepository(name);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java index 4c123fd..c7aabc9 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -285,7 +285,8 @@ public void testUsernamePatternNonRegex() { allow(local, READ, DEVS, "refs/sb/${username}/heads/*"); - ProjectControl u = util.user(local, "u", DEVS), d = util.user(local, "d", DEVS); + ProjectControl u = util.user(local, "u", DEVS); + ProjectControl d = util.user(local, "d", DEVS); assertFalse("u can't read", u.controlForRef("refs/sb/d/heads/foobar").isVisible()); assertTrue("d can read", d.controlForRef("refs/sb/d/heads/foobar").isVisible()); } @@ -294,7 +295,8 @@ public void testUsernamePatternWithRegex() { allow(local, READ, DEVS, "^refs/sb/${username}/heads/.*"); - ProjectControl u = util.user(local, "d.v", DEVS), d = util.user(local, "dev", DEVS); + ProjectControl u = util.user(local, "d.v", DEVS); + ProjectControl d = util.user(local, "dev", DEVS); assertFalse("u can't read", u.controlForRef("refs/sb/dev/heads/foobar").isVisible()); assertTrue("d can read", d.controlForRef("refs/sb/dev/heads/foobar").isVisible()); } @@ -316,7 +318,8 @@ allow(local, READ, DEVS, "^refs/heads/.*"); allow(parent, READ, ANONYMOUS_USERS, "^refs/heads/.*-QA-.*"); - ProjectControl u = util.user(local, DEVS), d = util.user(local, DEVS); + ProjectControl u = util.user(local, DEVS); + ProjectControl d = util.user(local, DEVS); assertTrue("u can read", u.controlForRef("refs/heads/foo-QA-bar").isVisible()); assertTrue("d can read", d.controlForRef("refs/heads/foo-QA-bar").isVisible()); }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java index e39700c..32bc9c6 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -21,9 +21,11 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.Lists; import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelValue; +import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountProjectWatch; @@ -81,15 +83,17 @@ import java.util.Set; public class Util { - public static AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin"); - public static AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs"); + public static final AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin"); + public static final AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs"); - public static final LabelType CR = category("Code-Review", - value(2, "Looks good to me, approved"), - value(1, "Looks good to me, but someone else must approve"), - value(0, "No score"), - value(-1, "I would prefer this is not merged as is"), - value(-2, "This shall not be merged")); + public static final LabelType codeReview() { + return category("Code-Review", + value(2, "Looks good to me, approved"), + value(1, "Looks good to me, but someone else must approve"), + value(0, "No score"), + value(-1, "I would prefer this is not merged as is"), + value(-2, "This shall not be merged")); + } public static LabelValue value(int value, String text) { return new LabelValue((short) value, text); @@ -138,6 +142,22 @@ project.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true) .getPermission(capabilityName, true) .add(rule); + if (GlobalCapability.hasRange(capabilityName)) { + PermissionRange.WithDefaults range = + GlobalCapability.getRange(capabilityName); + if (range != null) { + rule.setRange(range.getDefaultMin(), range.getDefaultMax()); + } + } + return rule; + } + + public static PermissionRule remove(ProjectConfig project, + String capabilityName, AccountGroup.UUID group) { + PermissionRule rule = newRule(project, group); + project.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true) + .getPermission(capabilityName, true) + .remove(rule); return rule; } @@ -197,7 +217,8 @@ Repository repo = repoManager.createRepository(allProjectsName); allProjects = new ProjectConfig(new Project.NameKey(allProjectsName.get())); allProjects.load(repo); - allProjects.getLabelSections().put(CR.getName(), CR); + LabelType cr = codeReview(); + allProjects.getLabelSections().put(cr.getName(), cr); add(allProjects); } catch (IOException | ConfigInvalidException e) { throw new RuntimeException(e);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java index 34588fa..fd36097 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -15,7 +15,6 @@ package com.google.gerrit.server.query.change; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assert_; import static com.google.common.truth.TruthJUnit.assume; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.TimeUnit.HOURS; @@ -23,7 +22,9 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.junit.Assert.fail; +import com.google.common.base.Function; import com.google.common.base.MoreObjects; +import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -32,12 +33,11 @@ import com.google.gerrit.common.Nullable; import com.google.gerrit.common.TimeUtil; import com.google.gerrit.extensions.api.GerritApi; +import com.google.gerrit.extensions.api.changes.Changes.QueryRequest; import com.google.gerrit.extensions.api.changes.HashtagsInput; import com.google.gerrit.extensions.api.changes.ReviewInput; -import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.restapi.BadRequestException; -import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Branch; @@ -51,11 +51,7 @@ import com.google.gerrit.server.account.AuthRequest; import com.google.gerrit.server.change.ChangeInserter; import com.google.gerrit.server.change.ChangeTriplet; -import com.google.gerrit.server.change.ChangesCollection; -import com.google.gerrit.server.change.PostReview; -import com.google.gerrit.server.change.RevisionResource; import com.google.gerrit.server.notedb.NotesMigration; -import com.google.gerrit.server.project.CreateProject; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.schema.SchemaCreator; import com.google.gerrit.server.util.RequestContext; @@ -63,12 +59,12 @@ import com.google.gerrit.testutil.ConfigSuite; import com.google.gerrit.testutil.InMemoryDatabase; import com.google.gerrit.testutil.InMemoryRepositoryManager; +import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import com.google.inject.util.Providers; -import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.revwalk.RevCommit; @@ -81,32 +77,37 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @Ignore @RunWith(ConfigSuite.class) public abstract class AbstractQueryChangesTest { - private static final TopLevelResource TLR = TopLevelResource.INSTANCE; + @ConfigSuite.Default + public static Config defaultConfig() { + return updateConfig(new Config()); + } @ConfigSuite.Config public static Config noteDbEnabled() { - return NotesMigration.allEnabledConfig(); + return updateConfig(NotesMigration.allEnabledConfig()); + } + + private static Config updateConfig(Config cfg) { + cfg.setInt("index", null, "maxPages", 10); + return cfg; } @ConfigSuite.Parameter public Config config; @Inject protected AccountManager accountManager; @Inject protected ChangeInserter.Factory changeFactory; - @Inject protected ChangesCollection changes; - @Inject protected CreateProject.Factory projectFactory; @Inject protected GerritApi gApi; - @Inject protected IdentifiedUser.RequestFactory userFactory; + @Inject protected IdentifiedUser.GenericFactory userFactory; @Inject protected InMemoryDatabase schemaFactory; @Inject protected InMemoryRepositoryManager repoManager; @Inject protected NotesMigration notesMigration; - @Inject protected PostReview postReview; @Inject protected ProjectControl.GenericFactory projectControlFactory; - @Inject protected Provider<QueryChanges> queryProvider; @Inject protected SchemaCreator schemaCreator; @Inject protected ThreadLocalRequestContext requestContext; @@ -122,10 +123,10 @@ @Before public void setUpInjector() throws Exception { - Injector injector = createInjector(); - injector.injectMembers(this); lifecycle = new LifecycleManager(); + Injector injector = createInjector(); lifecycle.add(injector); + injector.injectMembers(this); lifecycle.start(); db = schemaFactory.open(); @@ -135,12 +136,13 @@ Account userAccount = db.accounts().get(userId); userAccount.setPreferredEmail("user@example.com"); db.accounts().update(ImmutableList.of(userAccount)); - user = userFactory.create(userId); + user = userFactory.create(Providers.of(db), userId); requestContext.setContext(newRequestContext(userAccount.getId())); } private RequestContext newRequestContext(Account.Id requestUserId) { - final CurrentUser requestUser = userFactory.create(requestUserId); + final CurrentUser requestUser = + userFactory.create(Providers.of(db), requestUserId); return new RequestContext() { @Override public CurrentUser getCurrentUser() { @@ -189,58 +191,54 @@ @Test public void byId() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); - assertThat(query("12345")).isEmpty(); - assertResultEquals(change1, queryOne(change1.getId().get())); - assertResultEquals(change2, queryOne(change2.getId().get())); + assertQuery("12345"); + assertQuery(change1.getId().get(), change1); + assertQuery(change2.getId().get(), change2); } @Test public void byKey() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, null, null).insert(); String key = change.getKey().get(); - assertThat(query("I0000000000000000000000000000000000000000")).isEmpty(); + assertQuery("I0000000000000000000000000000000000000000"); for (int i = 0; i <= 36; i++) { String q = key.substring(0, 41 - i); - assertResultEquals("result for " + q, change, queryOne(q)); + assertQuery(q, change); } } @Test public void byTriplet() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, null, "branch").insert(); String k = change.getKey().get(); - assertResultEquals(change, queryOne("repo~branch~" + k)); - assertResultEquals(change, queryOne("change:repo~branch~" + k)); - assertResultEquals(change, queryOne("repo~refs/heads/branch~" + k)); - assertResultEquals(change, queryOne("change:repo~refs/heads/branch~" + k)); - assertResultEquals(change, queryOne("repo~branch~" + k.substring(0, 10))); - assertResultEquals(change, - queryOne("change:repo~branch~" + k.substring(0, 10))); + assertQuery("repo~branch~" + k, change); + assertQuery("change:repo~branch~" + k, change); + assertQuery("repo~refs/heads/branch~" + k, change); + assertQuery("change:repo~refs/heads/branch~" + k, change); + assertQuery("repo~branch~" + k.substring(0, 10), change); + assertQuery("change:repo~branch~" + k.substring(0, 10), change); - assertThat(query("foo~bar")).isEmpty(); + assertQuery("foo~bar"); assertBadQuery("change:foo~bar"); - assertThat(query("otherrepo~branch~" + k)).isEmpty(); - assertThat(query("change:otherrepo~branch~" + k)).isEmpty(); - assertThat(query("repo~otherbranch~" + k)).isEmpty(); - assertThat(query("change:repo~otherbranch~" + k)).isEmpty(); - assertThat(query("repo~branch~I0000000000000000000000000000000000000000")) - .isEmpty(); - assertThat(query( - "change:repo~branch~I0000000000000000000000000000000000000000")) - .isEmpty(); + assertQuery("otherrepo~branch~" + k); + assertQuery("change:otherrepo~branch~" + k); + assertQuery("repo~otherbranch~" + k); + assertQuery("change:repo~otherbranch~" + k); + assertQuery("repo~branch~I0000000000000000000000000000000000000000"); + assertQuery("change:repo~branch~I0000000000000000000000000000000000000000"); } @Test public void byStatus() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.NEW); @@ -250,16 +248,16 @@ change2.setStatus(Change.Status.MERGED); ins2.insert(); - assertResultEquals(change1, queryOne("status:new")); - assertResultEquals(change1, queryOne("status:NEW")); - assertResultEquals(change1, queryOne("is:new")); - assertResultEquals(change2, queryOne("status:merged")); - assertResultEquals(change2, queryOne("is:merged")); + assertQuery("status:new", change1); + assertQuery("status:NEW", change1); + assertQuery("is:new", change1); + assertQuery("status:merged", change2); + assertQuery("is:merged", change2); } @Test public void byStatusOpen() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.NEW); @@ -273,31 +271,23 @@ change3.setStatus(Change.Status.MERGED); ins3.insert(); - List<ChangeInfo> results; - results = query("status:open"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); - - assertThat(query("status:OPEN")).hasSize(2); - assertThat(query("status:o")).hasSize(2); - assertThat(query("status:op")).hasSize(2); - assertThat(query("status:ope")).hasSize(2); - assertThat(query("status:pending")).hasSize(2); - assertThat(query("status:PENDING")).hasSize(2); - assertThat(query("status:p")).hasSize(2); - assertThat(query("status:pe")).hasSize(2); - assertThat(query("status:pen")).hasSize(2); - - results = query("is:open"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + Change[] expected = new Change[] {change2, change1}; + assertQuery("status:open", expected); + assertQuery("status:OPEN", expected); + assertQuery("status:o", expected); + assertQuery("status:op", expected); + assertQuery("status:ope", expected); + assertQuery("status:pending", expected); + assertQuery("status:PENDING", expected); + assertQuery("status:p", expected); + assertQuery("status:pe", expected); + assertQuery("status:pen", expected); + assertQuery("is:open", expected); } @Test public void byStatusClosed() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.MERGED); @@ -311,29 +301,21 @@ change3.setStatus(Change.Status.NEW); ins3.insert(); - List<ChangeInfo> results; - results = query("status:closed"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); - - assertThat(query("status:CLOSED")).hasSize(2); - assertThat(query("status:c")).hasSize(2); - assertThat(query("status:cl")).hasSize(2); - assertThat(query("status:clo")).hasSize(2); - assertThat(query("status:clos")).hasSize(2); - assertThat(query("status:close")).hasSize(2); - assertThat(query("status:closed")).hasSize(2); - - results = query("is:closed"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + Change[] expected = new Change[] {change2, change1}; + assertQuery("status:closed", expected); + assertQuery("status:CLOSED", expected); + assertQuery("status:c", expected); + assertQuery("status:cl", expected); + assertQuery("status:clo", expected); + assertQuery("status:clos", expected); + assertQuery("status:close", expected); + assertQuery("status:closed", expected); + assertQuery("is:closed", expected); } @Test public void byStatusPrefix() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.NEW); @@ -343,109 +325,101 @@ change2.setStatus(Change.Status.MERGED); ins2.insert(); - assertResultEquals(change1, queryOne("status:n")); - assertResultEquals(change1, queryOne("status:ne")); - assertResultEquals(change1, queryOne("status:new")); - assertResultEquals(change1, queryOne("status:N")); - assertResultEquals(change1, queryOne("status:nE")); - assertResultEquals(change1, queryOne("status:neW")); + assertQuery("status:n", change1); + assertQuery("status:ne", change1); + assertQuery("status:new", change1); + assertQuery("status:N", change1); + assertQuery("status:nE", change1); + assertQuery("status:neW", change1); assertBadQuery("status:nx"); assertBadQuery("status:newx"); } @Test public void byCommit() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins = newChange(repo, null, null, null, null); ins.insert(); String sha = ins.getPatchSet().getRevision().get(); - assertThat(query("0000000000000000000000000000000000000000")).isEmpty(); + assertQuery("0000000000000000000000000000000000000000"); for (int i = 0; i <= 36; i++) { String q = sha.substring(0, 40 - i); - assertResultEquals("result for " + q, ins.getChange(), queryOne(q)); + assertQuery(q, ins.getChange()); } } @Test public void byOwner() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, userId.get(), null).insert(); int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) .getAccountId().get(); Change change2 = newChange(repo, null, null, user2, null).insert(); - assertResultEquals(change1, queryOne("owner:" + userId.get())); - assertResultEquals(change2, queryOne("owner:" + user2)); + assertQuery("owner:" + userId.get(), change1); + assertQuery("owner:" + user2, change2); } @Test public void byOwnerIn() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, userId.get(), null).insert(); int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) .getAccountId().get(); Change change2 = newChange(repo, null, null, user2, null).insert(); - assertResultEquals(change1, queryOne("ownerin:Administrators")); - List<ChangeInfo> results = query("ownerin:\"Registered Users\""); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("ownerin:Administrators", change1); + assertQuery("ownerin:\"Registered Users\"", change2, change1); } @Test public void byProject() throws Exception { - TestRepository<InMemoryRepository> repo1 = createProject("repo1"); - TestRepository<InMemoryRepository> repo2 = createProject("repo2"); + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); Change change1 = newChange(repo1, null, null, null, null).insert(); Change change2 = newChange(repo2, null, null, null, null).insert(); - assertThat(query("project:foo")).isEmpty(); - assertThat(query("project:repo")).isEmpty(); - assertResultEquals(change1, queryOne("project:repo1")); - assertResultEquals(change2, queryOne("project:repo2")); + assertQuery("project:foo"); + assertQuery("project:repo"); + assertQuery("project:repo1", change1); + assertQuery("project:repo2", change2); } @Test public void byProjectPrefix() throws Exception { - TestRepository<InMemoryRepository> repo1 = createProject("repo1"); - TestRepository<InMemoryRepository> repo2 = createProject("repo2"); + TestRepository<Repo> repo1 = createProject("repo1"); + TestRepository<Repo> repo2 = createProject("repo2"); Change change1 = newChange(repo1, null, null, null, null).insert(); Change change2 = newChange(repo2, null, null, null, null).insert(); - assertThat(query("projects:foo")).isEmpty(); - assertResultEquals(change1, queryOne("projects:repo1")); - assertResultEquals(change2, queryOne("projects:repo2")); - - List<ChangeInfo> results; - results = query("projects:repo"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("projects:foo"); + assertQuery("projects:repo1", change1); + assertQuery("projects:repo2", change2); + assertQuery("projects:repo", change2, change1); } @Test public void byBranchAndRef() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, "master").insert(); Change change2 = newChange(repo, null, null, null, "branch").insert(); - assertThat(query("branch:foo")).isEmpty(); - assertResultEquals(change1, queryOne("branch:master")); - assertResultEquals(change1, queryOne("branch:refs/heads/master")); - assertThat(query("ref:master")).isEmpty(); - assertResultEquals(change1, queryOne("ref:refs/heads/master")); - assertResultEquals(change1, queryOne("branch:refs/heads/master")); - assertResultEquals(change2, queryOne("branch:branch")); - assertResultEquals(change2, queryOne("branch:refs/heads/branch")); - assertThat(query("ref:branch")).isEmpty(); - assertResultEquals(change2, queryOne("ref:refs/heads/branch")); + assertQuery("branch:foo"); + assertQuery("branch:master", change1); + assertQuery("branch:refs/heads/master", change1); + assertQuery("ref:master"); + assertQuery("ref:refs/heads/master", change1); + assertQuery("branch:refs/heads/master", change1); + assertQuery("branch:branch", change2); + assertQuery("branch:refs/heads/branch", change2); + assertQuery("ref:branch"); + assertQuery("ref:refs/heads/branch", change2); } @Test public void byTopic() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setTopic("feature1"); @@ -458,28 +432,29 @@ Change change3 = newChange(repo, null, null, null, null).insert(); - assertThat(query("topic:foo")).isEmpty(); - assertResultEquals(change1, queryOne("topic:feature1")); - assertResultEquals(change2, queryOne("topic:feature2")); - assertResultEquals(change3, queryOne("topic:\"\"")); + assertQuery("topic:foo"); + assertQuery("topic:feature1", change1); + assertQuery("topic:feature2", change2); + assertQuery("topic:feature", change2, change1); + assertQuery("topic:\"\"", change3, change2, change1); } @Test public void byMessageExact() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit1 = repo.parseBody(repo.commit().message("one").create()); Change change1 = newChange(repo, commit1, null, null, null).insert(); RevCommit commit2 = repo.parseBody(repo.commit().message("two").create()); Change change2 = newChange(repo, commit2, null, null, null).insert(); - assertThat(query("message:foo")).isEmpty(); - assertResultEquals(change1, queryOne("message:one")); - assertResultEquals(change2, queryOne("message:two")); + assertQuery("message:foo"); + assertQuery("message:one", change1); + assertQuery("message:two", change2); } @Test public void fullTextWithNumbers() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit1 = repo.parseBody(repo.commit().message("12345 67890").create()); Change change1 = newChange(repo, commit1, null, null, null).insert(); @@ -487,65 +462,61 @@ repo.parseBody(repo.commit().message("12346 67891").create()); Change change2 = newChange(repo, commit2, null, null, null).insert(); - assertThat(query("message:1234")).isEmpty(); - assertResultEquals(change1, queryOne("message:12345")); - assertResultEquals(change2, queryOne("message:12346")); + assertQuery("message:1234"); + assertQuery("message:12345", change1); + assertQuery("message:12346", change2); } @Test public void byLabel() throws Exception { accountManager.authenticate(AuthRequest.forUser("anotheruser")); - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins = newChange(repo, null, null, null, null); Change change = ins.insert(); - ReviewInput input = new ReviewInput(); - input.message = "toplevel"; - input.labels = ImmutableMap.<String, Short> of("Code-Review", (short) 1); - postReview.apply(new RevisionResource( - changes.parse(change.getId()), ins.getPatchSet()), input); + gApi.changes().id(change.getId().get()).current() + .review(new ReviewInput().label("Code-Review", 1)); - assertThat(query("label:Code-Review=-2")).isEmpty(); - assertThat(query("label:Code-Review-2")).isEmpty(); - assertThat(query("label:Code-Review=-1")).isEmpty(); - assertThat(query("label:Code-Review-1")).isEmpty(); - assertThat(query("label:Code-Review=0")).isEmpty(); - assertResultEquals(change, queryOne("label:Code-Review=+1")); - assertResultEquals(change, queryOne("label:Code-Review=1")); - assertResultEquals(change, queryOne("label:Code-Review+1")); - assertThat(query("label:Code-Review=+2")).isEmpty(); - assertThat(query("label:Code-Review=2")).isEmpty(); - assertThat(query("label:Code-Review+2")).isEmpty(); + assertQuery("label:Code-Review=-2"); + assertQuery("label:Code-Review-2"); + assertQuery("label:Code-Review=-1"); + assertQuery("label:Code-Review-1"); + assertQuery("label:Code-Review=0"); + assertQuery("label:Code-Review=+1", change); + assertQuery("label:Code-Review=1", change); + assertQuery("label:Code-Review+1", change); + assertQuery("label:Code-Review=+2"); + assertQuery("label:Code-Review=2"); + assertQuery("label:Code-Review+2"); - assertResultEquals(change, queryOne("label:Code-Review>=0")); - assertResultEquals(change, queryOne("label:Code-Review>0")); - assertResultEquals(change, queryOne("label:Code-Review>=1")); - assertThat(query("label:Code-Review>1")).isEmpty(); - assertThat(query("label:Code-Review>=2")).isEmpty(); + assertQuery("label:Code-Review>=0", change); + assertQuery("label:Code-Review>0", change); + assertQuery("label:Code-Review>=1", change); + assertQuery("label:Code-Review>1"); + assertQuery("label:Code-Review>=2"); - assertResultEquals(change, queryOne("label: Code-Review<=2")); - assertResultEquals(change, queryOne("label: Code-Review<2")); - assertResultEquals(change, queryOne("label: Code-Review<=1")); - assertThat(query("label:Code-Review<1")).isEmpty(); - assertThat(query("label:Code-Review<=0")).isEmpty(); + assertQuery("label: Code-Review<=2", change); + assertQuery("label: Code-Review<2", change); + assertQuery("label: Code-Review<=1", change); + assertQuery("label:Code-Review<1"); + assertQuery("label:Code-Review<=0"); - assertThat(query("label:Code-Review=+1,anotheruser")).isEmpty(); - assertResultEquals(change, queryOne("label:Code-Review=+1,user")); - assertResultEquals(change, queryOne("label:Code-Review=+1,user=user")); - assertResultEquals(change, queryOne("label:Code-Review=+1,Administrators")); - assertResultEquals(change, queryOne("label:Code-Review=+1,group=Administrators")); + assertQuery("label:Code-Review=+1,anotheruser"); + assertQuery("label:Code-Review=+1,user", change); + assertQuery("label:Code-Review=+1,user=user", change); + assertQuery("label:Code-Review=+1,Administrators", change); + assertQuery("label:Code-Review=+1,group=Administrators", change); } @Test public void limit() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change last = null; int n = 5; for (int i = 0; i < n; i++) { last = newChange(repo, null, null, null, null).insert(); } - List<ChangeInfo> results; for (int i = 1; i <= n + 2; i++) { int expectedSize; Boolean expectedMoreChanges; @@ -556,86 +527,62 @@ expectedSize = n; expectedMoreChanges = null; } - results = query("status:new limit:" + i); - String msg = "i=" + i; - assert_().withFailureMessage(msg).that(results).hasSize(expectedSize); - assertResultEquals(last, results.get(0)); - assert_().withFailureMessage(msg) - .that(results.get(results.size() - 1)._moreChanges) + String q = "status:new limit:" + i; + List<ChangeInfo> results = newQuery(q).get(); + assertThat(results).named(q).hasSize(expectedSize); + assertThat(results.get(results.size() - 1)._moreChanges).named(q) .isEqualTo(expectedMoreChanges); + assertThat(results.get(0)._number).isEqualTo(last.getId().get()); } } @Test public void start() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); List<Change> changes = Lists.newArrayList(); for (int i = 0; i < 2; i++) { changes.add(newChange(repo, null, null, null, null).insert()); } - QueryChanges q; - List<ChangeInfo> results; - results = query("status:new"); - assertThat(results).hasSize(2); - assertResultEquals(changes.get(1), results.get(0)); - assertResultEquals(changes.get(0), results.get(1)); - - q = newQuery("status:new"); - q.setStart(1); - results = query(q); - assertThat(results).hasSize(1); - assertResultEquals(changes.get(0), results.get(0)); - - q = newQuery("status:new"); - q.setStart(2); - results = query(q); - assertThat(results).isEmpty(); - - q = newQuery("status:new"); - q.setStart(3); - results = query(q); - assertThat(results).isEmpty(); + assertQuery("status:new", changes.get(1), changes.get(0)); + assertQuery(newQuery("status:new").withStart(1), changes.get(0)); + assertQuery(newQuery("status:new").withStart(2)); + assertQuery(newQuery("status:new").withStart(3)); } @Test public void startWithLimit() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); List<Change> changes = Lists.newArrayList(); for (int i = 0; i < 3; i++) { changes.add(newChange(repo, null, null, null, null).insert()); } - QueryChanges q; - List<ChangeInfo> results; - results = query("status:new limit:2"); - assertThat(results).hasSize(2); - assertResultEquals(changes.get(2), results.get(0)); - assertResultEquals(changes.get(1), results.get(1)); + assertQuery("status:new limit:2", changes.get(2), changes.get(1)); + assertQuery( + newQuery("status:new limit:2").withStart(1), + changes.get(1), changes.get(0)); + assertQuery(newQuery("status:new limit:2").withStart(2), changes.get(0)); + assertQuery(newQuery("status:new limit:2").withStart(3)); + } - q = newQuery("status:new limit:2"); - q.setStart(1); - results = query(q); - assertThat(results).hasSize(2); - assertResultEquals(changes.get(1), results.get(0)); - assertResultEquals(changes.get(0), results.get(1)); + @Test + public void maxPages() throws Exception { + TestRepository<Repo> repo = createProject("repo"); + Change change = newChange(repo, null, null, null, null).insert(); - q = newQuery("status:new limit:2"); - q.setStart(2); - results = query(q); - assertThat(results).hasSize(1); - assertResultEquals(changes.get(0), results.get(0)); - - q = newQuery("status:new limit:2"); - q.setStart(3); - results = query(q); - assertThat(results).isEmpty(); + QueryRequest query = newQuery("status:new").withLimit(10); + assertQuery(query, change); + assertQuery(query.withStart(1)); + assertQuery(query.withStart(99)); + assertBadQuery(query.withStart(100)); + assertQuery(query.withLimit(100).withStart(100)); } @Test public void updateOrder() throws Exception { clockStepMs = MILLISECONDS.convert(2, MINUTES); - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); List<ChangeInserter> inserters = Lists.newArrayList(); List<Change> changes = Lists.newArrayList(); for (int i = 0; i < 5; i++) { @@ -644,93 +591,68 @@ } for (int i : ImmutableList.of(2, 0, 1, 4, 3)) { - ReviewInput input = new ReviewInput(); - input.message = "modifying " + i; - postReview.apply( - new RevisionResource( - this.changes.parse(changes.get(i).getId()), - inserters.get(i).getPatchSet()), - input); - changes.set(i, db.changes().get(changes.get(i).getId())); + gApi.changes().id(changes.get(i).getId().get()).current() + .review(new ReviewInput().message("modifying " + i)); } - List<ChangeInfo> results = query("status:new"); - assertThat(results).hasSize(5); - assertResultEquals(changes.get(3), results.get(0)); - assertResultEquals(changes.get(4), results.get(1)); - assertResultEquals(changes.get(1), results.get(2)); - assertResultEquals(changes.get(0), results.get(3)); - assertResultEquals(changes.get(2), results.get(4)); + assertQuery( + "status:new", + changes.get(3), + changes.get(4), + changes.get(1), + changes.get(0), + changes.get(2)); } @Test public void updatedOrderWithMinuteResolution() throws Exception { clockStepMs = MILLISECONDS.convert(2, MINUTES); - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.insert(); Change change2 = newChange(repo, null, null, null, null).insert(); - assertThat(lastUpdatedMs(change1) < lastUpdatedMs(change2)).isTrue(); + assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2)); + assertQuery("status:new", change2, change1); - List<ChangeInfo> results; - results = query("status:new"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); - - ReviewInput input = new ReviewInput(); - input.message = "toplevel"; - postReview.apply(new RevisionResource( - changes.parse(change1.getId()), ins1.getPatchSet()), input); + gApi.changes().id(change1.getId().get()).current() + .review(new ReviewInput()); change1 = db.changes().get(change1.getId()); - assertThat(lastUpdatedMs(change1) > lastUpdatedMs(change2)).isTrue(); - assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2) - > MILLISECONDS.convert(1, MINUTES)).isTrue(); + assertThat(lastUpdatedMs(change1)).isGreaterThan(lastUpdatedMs(change2)); + assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2)) + .isGreaterThan(MILLISECONDS.convert(1, MINUTES)); - results = query("status:new"); - assertThat(results).hasSize(2); // change1 moved to the top. - assertResultEquals(change1, results.get(0)); - assertResultEquals(change2, results.get(1)); + assertQuery("status:new", change1, change2); } @Test public void updatedOrderWithSubMinuteResolution() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.insert(); Change change2 = newChange(repo, null, null, null, null).insert(); - assertThat(lastUpdatedMs(change1) < lastUpdatedMs(change2)).isTrue(); + assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2)); - List<ChangeInfo> results; - results = query("status:new"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("status:new", change2, change1); - ReviewInput input = new ReviewInput(); - input.message = "toplevel"; - postReview.apply(new RevisionResource( - changes.parse(change1.getId()), ins1.getPatchSet()), input); + gApi.changes().id(change1.getId().get()).current() + .review(new ReviewInput()); change1 = db.changes().get(change1.getId()); - assertThat(lastUpdatedMs(change1) > lastUpdatedMs(change2)).isTrue(); - assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2) - < MILLISECONDS.convert(1, MINUTES)).isTrue(); + assertThat(lastUpdatedMs(change1)).isGreaterThan(lastUpdatedMs(change2)); + assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2)) + .isLessThan(MILLISECONDS.convert(1, MINUTES)); - results = query("status:new"); - assertThat(results).hasSize(2); // change1 moved to the top. - assertResultEquals(change1, results.get(0)); - assertResultEquals(change2, results.get(1)); + assertQuery("status:new", change1, change2); } @Test public void filterOutMoreThanOnePageOfResults() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, userId.get(), null).insert(); int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) .getAccountId().get(); @@ -738,88 +660,87 @@ newChange(repo, null, null, user2, null).insert(); } - assertResultEquals(change, queryOne("status:new ownerin:Administrators")); - assertResultEquals(change, - queryOne("status:new ownerin:Administrators limit:2")); + assertQuery("status:new ownerin:Administrators", change); + assertQuery("status:new ownerin:Administrators limit:2", change); } @Test public void filterOutAllResults() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) .getAccountId().get(); for (int i = 0; i < 5; i++) { newChange(repo, null, null, user2, null).insert(); } - assertThat(query("status:new ownerin:Administrators")).isEmpty(); - assertThat(query("status:new ownerin:Administrators limit:2")).isEmpty(); + assertQuery("status:new ownerin:Administrators"); + assertQuery("status:new ownerin:Administrators limit:2"); } @Test public void byFileExact() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit = repo.parseBody( repo.commit().message("one") .add("dir/file1", "contents1").add("dir/file2", "contents2") .create()); Change change = newChange(repo, commit, null, null, null).insert(); - assertThat(query("file:file")).isEmpty(); - assertResultEquals(change, queryOne("file:dir")); - assertResultEquals(change, queryOne("file:file1")); - assertResultEquals(change, queryOne("file:file2")); - assertResultEquals(change, queryOne("file:dir/file1")); - assertResultEquals(change, queryOne("file:dir/file2")); + assertQuery("file:file"); + assertQuery("file:dir", change); + assertQuery("file:file1", change); + assertQuery("file:file2", change); + assertQuery("file:dir/file1", change); + assertQuery("file:dir/file2", change); } @Test public void byFileRegex() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit = repo.parseBody( repo.commit().message("one") .add("dir/file1", "contents1").add("dir/file2", "contents2") .create()); Change change = newChange(repo, commit, null, null, null).insert(); - assertThat(query("file:.*file.*")).isEmpty(); - assertThat(query("file:^file.*")).isEmpty(); // Whole path only. - assertResultEquals(change, queryOne("file:^dir.file.*")); + assertQuery("file:.*file.*"); + assertQuery("file:^file.*"); // Whole path only. + assertQuery("file:^dir.file.*", change); } @Test public void byPathExact() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit = repo.parseBody( repo.commit().message("one") .add("dir/file1", "contents1").add("dir/file2", "contents2") .create()); Change change = newChange(repo, commit, null, null, null).insert(); - assertThat(query("path:file")).isEmpty(); - assertThat(query("path:dir")).isEmpty(); - assertThat(query("path:file1")).isEmpty(); - assertThat(query("path:file2")).isEmpty(); - assertResultEquals(change, queryOne("path:dir/file1")); - assertResultEquals(change, queryOne("path:dir/file2")); + assertQuery("path:file"); + assertQuery("path:dir"); + assertQuery("path:file1"); + assertQuery("path:file2"); + assertQuery("path:dir/file1", change); + assertQuery("path:dir/file2", change); } @Test public void byPathRegex() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit = repo.parseBody( repo.commit().message("one") .add("dir/file1", "contents1").add("dir/file2", "contents2") .create()); Change change = newChange(repo, commit, null, null, null).insert(); - assertThat(query("path:.*file.*")).isEmpty(); - assertResultEquals(change, queryOne("path:^dir.file.*")); + assertQuery("path:.*file.*"); + assertQuery("path:^dir.file.*", change); } @Test public void byComment() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins = newChange(repo, null, null, null, null); Change change = ins.insert(); @@ -830,99 +751,74 @@ comment.message = "inline"; input.comments = ImmutableMap.<String, List<ReviewInput.CommentInput>> of( Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput> of(comment)); - postReview.apply(new RevisionResource( - changes.parse(change.getId()), ins.getPatchSet()), input); + gApi.changes().id(change.getId().get()).current().review(input); - assertThat(query("comment:foo")).isEmpty(); - assertResultEquals(change, queryOne("comment:toplevel")); - assertResultEquals(change, queryOne("comment:inline")); + assertQuery("comment:foo"); + assertQuery("comment:toplevel", change); + assertQuery("comment:inline", change); } @Test public void byAge() throws Exception { long thirtyHours = MILLISECONDS.convert(30, HOURS); clockStepMs = thirtyHours; - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); clockStepMs = 0; // Queried by AgePredicate constructor. long now = TimeUtil.nowMs(); - assertThat(lastUpdatedMs(change2) - lastUpdatedMs(change1)).isEqualTo(thirtyHours); + assertThat(lastUpdatedMs(change2) - lastUpdatedMs(change1)) + .isEqualTo(thirtyHours); assertThat(now - lastUpdatedMs(change2)).isEqualTo(thirtyHours); assertThat(TimeUtil.nowMs()).isEqualTo(now); - assertThat(query("-age:1d")).isEmpty(); - assertThat(query("-age:" + (30 * 60 - 1) + "m")).isEmpty(); - assertResultEquals(change2, queryOne("-age:2d")); - - List<ChangeInfo> results; - results = query("-age:3d"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); - - assertThat(query("age:3d")).isEmpty(); - assertResultEquals(change1, queryOne("age:2d")); - - results = query("age:1d"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("-age:1d"); + assertQuery("-age:" + (30 * 60 - 1) + "m"); + assertQuery("-age:2d", change2); + assertQuery("-age:3d", change2, change1); + assertQuery("age:3d"); + assertQuery("age:2d", change1); + assertQuery("age:1d", change2, change1); } @Test public void byBefore() throws Exception { clockStepMs = MILLISECONDS.convert(30, HOURS); - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); clockStepMs = 0; - assertThat(query("before:2009-09-29")).isEmpty(); - assertThat(query("before:2009-09-30")).isEmpty(); - assertThat(query("before:\"2009-09-30 16:59:00 -0400\"")).isEmpty(); - assertThat(query("before:\"2009-09-30 20:59:00 -0000\"")).isEmpty(); - assertThat(query("before:\"2009-09-30 20:59:00\"")).isEmpty(); - assertResultEquals(change1, - queryOne("before:\"2009-09-30 17:02:00 -0400\"")); - assertResultEquals(change1, - queryOne("before:\"2009-10-01 21:02:00 -0000\"")); - assertResultEquals(change1, - queryOne("before:\"2009-10-01 21:02:00\"")); - assertResultEquals(change1, queryOne("before:2009-10-01")); - - List<ChangeInfo> results; - results = query("before:2009-10-03"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("before:2009-09-29"); + assertQuery("before:2009-09-30"); + assertQuery("before:\"2009-09-30 16:59:00 -0400\""); + assertQuery("before:\"2009-09-30 20:59:00 -0000\""); + assertQuery("before:\"2009-09-30 20:59:00\""); + assertQuery("before:\"2009-09-30 17:02:00 -0400\"", change1); + assertQuery("before:\"2009-10-01 21:02:00 -0000\"", change1); + assertQuery("before:\"2009-10-01 21:02:00\"", change1); + assertQuery("before:2009-10-01", change1); + assertQuery("before:2009-10-03", change2, change1); } @Test public void byAfter() throws Exception { clockStepMs = MILLISECONDS.convert(30, HOURS); - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); clockStepMs = 0; - assertThat(query("after:2009-10-03")).isEmpty(); - assertResultEquals(change2, - queryOne("after:\"2009-10-01 20:59:59 -0400\"")); - assertResultEquals(change2, - queryOne("after:\"2009-10-01 20:59:59 -0000\"")); - assertResultEquals(change2, queryOne("after:2009-10-01")); - - List<ChangeInfo> results; - results = query("after:2009-09-30"); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery("after:2009-10-03"); + assertQuery("after:\"2009-10-01 20:59:59 -0400\"", change2); + assertQuery("after:\"2009-10-01 20:59:59 -0000\"", change2); + assertQuery("after:2009-10-01", change2); + assertQuery("after:2009-09-30", change2, change1); } @Test public void bySize() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); // added = 3, deleted = 0, delta = 3 RevCommit commit1 = repo.parseBody( @@ -934,32 +830,32 @@ Change change1 = newChange(repo, commit1, null, null, null).insert(); Change change2 = newChange(repo, commit2, null, null, null).insert(); - assertThat(query("added:>4")).isEmpty(); - assertResultEquals(change1, queryOne("added:3")); - assertResultEquals(change1, queryOne("added:>2")); - assertResultEquals(change1, queryOne("added:>=3")); - assertResultEquals(change2, queryOne("added:<1")); - assertResultEquals(change2, queryOne("added:<=0")); + assertQuery("added:>4"); + assertQuery("added:3", change1); + assertQuery("added:>2", change1); + assertQuery("added:>=3", change1); + assertQuery("added:<1", change2); + assertQuery("added:<=0", change2); - assertThat(query("deleted:>3")).isEmpty(); - assertResultEquals(change2, queryOne("deleted:2")); - assertResultEquals(change2, queryOne("deleted:>1")); - assertResultEquals(change2, queryOne("deleted:>=2")); - assertResultEquals(change1, queryOne("deleted:<1")); - assertResultEquals(change1, queryOne("deleted:<=0")); + assertQuery("deleted:>3"); + assertQuery("deleted:2", change2); + assertQuery("deleted:>1", change2); + assertQuery("deleted:>=2", change2); + assertQuery("deleted:<1", change1); + assertQuery("deleted:<=0", change1); for (String str : Lists.newArrayList("delta", "size")) { - assertThat(query(str + ":<2")).isEmpty(); - assertResultEquals(change1, queryOne(str + ":3")); - assertResultEquals(change1, queryOne(str + ":>2")); - assertResultEquals(change1, queryOne(str + ":>=3")); - assertResultEquals(change2, queryOne(str + ":<3")); - assertResultEquals(change2, queryOne(str + ":<=2")); + assertQuery(str + ":<2"); + assertQuery(str + ":3", change1); + assertQuery(str + ":>2", change1); + assertQuery(str + ":>=3", change1); + assertQuery(str + ":<3", change2); + assertQuery(str + ":<=2", change2); } } private List<Change> setUpHashtagChanges() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); @@ -977,34 +873,31 @@ public void byHashtagWithNotedb() throws Exception { assume().that(notesMigration.enabled()).isTrue(); List<Change> changes = setUpHashtagChanges(); - List<ChangeInfo> results = query("hashtag:foo"); - assertThat(results).hasSize(2); - assertResultEquals(changes.get(1), results.get(0)); - assertResultEquals(changes.get(0), results.get(1)); - assertResultEquals(changes.get(1), queryOne("hashtag:bar")); - assertResultEquals(changes.get(1), queryOne("hashtag:\"a tag\"")); - assertResultEquals(changes.get(1), queryOne("hashtag:\"a tag \"")); - assertResultEquals(changes.get(1), queryOne("hashtag:\" a tag \"")); - assertResultEquals(changes.get(1), queryOne("hashtag:\"#a tag\"")); - assertResultEquals(changes.get(1), queryOne("hashtag:\"# #a tag\"")); + assertQuery("hashtag:foo", changes.get(1), changes.get(0)); + assertQuery("hashtag:bar", changes.get(1)); + assertQuery("hashtag:\"a tag\"", changes.get(1)); + assertQuery("hashtag:\"a tag \"", changes.get(1)); + assertQuery("hashtag:\" a tag \"", changes.get(1)); + assertQuery("hashtag:\"#a tag\"", changes.get(1)); + assertQuery("hashtag:\"# #a tag\"", changes.get(1)); } @Test public void byHashtagWithoutNotedb() throws Exception { assume().that(notesMigration.enabled()).isFalse(); setUpHashtagChanges(); - assertThat(query("hashtag:foo")).isEmpty(); - assertThat(query("hashtag:bar")).isEmpty(); - assertThat(query("hashtag:\" bar \"")).isEmpty(); - assertThat(query("hashtag:\"a tag\"")).isEmpty(); - assertThat(query("hashtag:\" a tag \"")).isEmpty(); - assertThat(query("hashtag:#foo")).isEmpty(); - assertThat(query("hashtag:\"# #foo\"")).isEmpty(); + assertQuery("hashtag:foo"); + assertQuery("hashtag:bar"); + assertQuery("hashtag:\" bar \""); + assertQuery("hashtag:\"a tag\""); + assertQuery("hashtag:\" a tag \""); + assertQuery("hashtag:#foo"); + assertQuery("hashtag:\"# #foo\""); } @Test public void byDefault() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); @@ -1023,8 +916,7 @@ ReviewInput ri4 = new ReviewInput(); ri4.message = "toplevel"; ri4.labels = ImmutableMap.<String, Short> of("Code-Review", (short) 1); - postReview.apply(new RevisionResource( - changes.parse(change4.getId()), ins4.getPatchSet()), ri4); + gApi.changes().id(change4.getId().get()).current().review(ri4); ChangeInserter ins5 = newChange(repo, null, null, null, null); Change change5 = ins5.getChange(); @@ -1033,24 +925,25 @@ Change change6 = newChange(repo, null, null, null, "branch6").insert(); - assertResultEquals(change1, - queryOne(Integer.toString(change1.getId().get()))); - assertResultEquals(change1, queryOne(ChangeTriplet.format(change1))); - assertResultEquals(change2, queryOne("foosubject")); - assertResultEquals(change3, queryOne("Foo.java")); - assertResultEquals(change4, queryOne("Code-Review+1")); - assertResultEquals(change4, queryOne("toplevel")); - assertResultEquals(change5, queryOne("feature5")); - assertResultEquals(change6, queryOne("branch6")); - assertResultEquals(change6, queryOne("refs/heads/branch6")); + assertQuery(change1.getId().get(), change1); + assertQuery(ChangeTriplet.format(change1), change1); + assertQuery("foosubject", change2); + assertQuery("Foo.java", change3); + assertQuery("Code-Review+1", change4); + assertQuery("toplevel", change4); + assertQuery("feature5", change5); + assertQuery("branch6", change6); + assertQuery("refs/heads/branch6", change6); - assertThat(query("user@example.com")).hasSize(6); - assertThat(query("repo")).hasSize(6); + Change[] expected = + new Change[] {change6, change5, change4, change3, change2, change1}; + assertQuery("user@example.com", expected); + assertQuery("repo", expected); } @Test public void implicitVisibleTo() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, userId.get(), null).insert(); ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null); Change change2 = ins2.getChange(); @@ -1058,20 +951,17 @@ ins2.insert(); String q = "project:repo"; - List<ChangeInfo> results = query(q); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery(q, change2, change1); // Second user cannot see first user's drafts. requestContext.setContext(newRequestContext(accountManager .authenticate(AuthRequest.forUser("anotheruser")).getAccountId())); - assertResultEquals(change1, queryOne(q)); + assertQuery(q, change1); } @Test public void explicitVisibleTo() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, userId.get(), null).insert(); ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null); Change change2 = ins2.getChange(); @@ -1079,20 +969,98 @@ ins2.insert(); String q = "project:repo"; - List<ChangeInfo> results = query(q); - assertThat(results).hasSize(2); - assertResultEquals(change2, results.get(0)); - assertResultEquals(change1, results.get(1)); + assertQuery(q, change2, change1); // Second user cannot see first user's drafts. Account.Id user2 = accountManager .authenticate(AuthRequest.forUser("anotheruser")) .getAccountId(); - assertResultEquals(change1, queryOne(q + " visibleto:" + user2.get())); + assertQuery(q + " visibleto:" + user2.get(), change1); + } + + @Test + public void byCommentBy() throws Exception { + TestRepository<Repo> repo = createProject("repo"); + Change change1 = newChange(repo, null, null, null, null).insert(); + Change change2 = newChange(repo, null, null, null, null).insert(); + + int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) + .getAccountId().get(); + + ReviewInput input = new ReviewInput(); + input.message = "toplevel"; + ReviewInput.CommentInput comment = new ReviewInput.CommentInput(); + comment.line = 1; + comment.message = "inline"; + input.comments = ImmutableMap.<String, List<ReviewInput.CommentInput>> of( + Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput> of(comment)); + gApi.changes().id(change1.getId().get()).current().review(input); + + input = new ReviewInput(); + input.message = "toplevel"; + gApi.changes().id(change2.getId().get()).current().review(input); + + assertQuery("commentby:" + userId.get(), change2, change1); + assertQuery("commentby:" + user2); + } + + @Test + public void byFrom() throws Exception { + TestRepository<Repo> repo = createProject("repo"); + Change change1 = newChange(repo, null, null, null, null).insert(); + + int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")) + .getAccountId().get(); + ChangeInserter ins2 = newChange(repo, null, null, user2, null); + Change change2 = ins2.insert(); + + ReviewInput input = new ReviewInput(); + input.message = "toplevel"; + ReviewInput.CommentInput comment = new ReviewInput.CommentInput(); + comment.line = 1; + comment.message = "inline"; + input.comments = ImmutableMap.<String, List<ReviewInput.CommentInput>> of( + Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput> of(comment)); + gApi.changes().id(change2.getId().get()).current().review(input); + + assertQuery("from:" + userId.get(), change2, change1); + assertQuery("from:" + user2, change2); + } + + @Test + public void conflicts() throws Exception { + TestRepository<Repo> repo = createProject("repo"); + RevCommit commit1 = repo.parseBody( + repo.commit() + .add("file1", "contents1") + .add("dir/file2", "contents2") + .add("dir/file3", "contents3") + .create()); + RevCommit commit2 = repo.parseBody( + repo.commit() + .add("file1", "contents1") + .create()); + RevCommit commit3 = repo.parseBody( + repo.commit() + .add("dir/file2", "contents2 different") + .create()); + RevCommit commit4 = repo.parseBody( + repo.commit() + .add("file4", "contents4") + .create()); + Change change1 = newChange(repo, commit1, null, null, null).insert(); + Change change2 = newChange(repo, commit2, null, null, null).insert(); + Change change3 = newChange(repo, commit3, null, null, null).insert(); + Change change4 = newChange(repo, commit4, null, null, null).insert(); + + assertQuery("conflicts:" + change1.getId().get(), change3); + assertQuery("conflicts:" + change2.getId().get()); + assertQuery("conflicts:" + change3.getId().get(), change1); + assertQuery("conflicts:" + change4.getId().get()); } protected ChangeInserter newChange( - TestRepository<InMemoryRepository> repo, + TestRepository<Repo> repo, @Nullable RevCommit commit, @Nullable String key, @Nullable Integer owner, @Nullable String branch) throws Exception { if (commit == null) { @@ -1120,75 +1088,69 @@ Change change = new Change(new Change.Key(key), id, ownerId, new Branch.NameKey(project, branch), TimeUtil.nowTs()); + IdentifiedUser user = userFactory.create(Providers.of(db), ownerId); return changeFactory.create( - projectControlFactory.controlFor(project, userFactory.create(ownerId)), + projectControlFactory.controlFor(project, user), change, commit); } - protected void assertResultEquals(Change expected, ChangeInfo actual) { - assertThat(actual._number).isEqualTo(expected.getId().get()); - } - - protected void assertResultEquals(String message, Change expected, - ChangeInfo actual) { - assert_().withFailureMessage(message).that(actual._number) - .isEqualTo(expected.getId().get()); - } - protected void assertBadQuery(Object query) throws Exception { + assertBadQuery(newQuery(query)); + } + + protected void assertBadQuery(QueryRequest query) throws Exception { try { - query(query); + query.get(); fail("expected BadRequestException for query: " + query); } catch (BadRequestException e) { // Expected. } } - protected TestRepository<InMemoryRepository> createProject(String name) - throws Exception { - CreateProject create = projectFactory.create(name); - create.apply(TLR, new ProjectInput()); + protected TestRepository<Repo> createProject(String name) throws Exception { + gApi.projects().create(name).get(); return new TestRepository<>( repoManager.openRepository(new Project.NameKey(name))); } - protected QueryChanges newQuery(Object query) { - QueryChanges q = queryProvider.get(); - q.addQuery(query.toString()); - return q; + protected QueryRequest newQuery(Object query) { + return gApi.changes().query(query.toString()); } - @SuppressWarnings({"rawtypes", "unchecked"}) - protected List<ChangeInfo> query(QueryChanges q) throws Exception { - Object result = q.apply(TLR); - assert_() - .withFailureMessage( - String.format("expected List<ChangeInfo>, found %s for [%s]", - result, q.getQuery(0))).that(result).isInstanceOf(List.class); - List results = (List) result; - if (!results.isEmpty()) { - assert_() - .withFailureMessage( - String.format("expected ChangeInfo, found %s for [%s]", result, - q.getQuery(0))).that(results.get(0)) - .isInstanceOf(ChangeInfo.class); - } - return (List<ChangeInfo>) result; + protected void assertQuery(Object query, Change... changes) + throws Exception { + assertQuery(newQuery(query), changes); } - protected List<ChangeInfo> query(Object query) throws Exception { + protected void assertQuery(QueryRequest query, Change... changes) + throws Exception { + assertThat(query(query)).named(query.toString()) + .containsExactlyElementsIn(ids(changes)).inOrder(); + } + + protected List<Integer> query(Object query) throws Exception { return query(newQuery(query)); } - protected ChangeInfo queryOne(Object query) throws Exception { - List<ChangeInfo> results = query(query); - assert_() - .withFailureMessage( - String.format( - "expected singleton List<ChangeInfo>, found %s for [%s]", - results, query)).that(results).hasSize(1); - return results.get(0); + protected static List<Integer> query(QueryRequest query) throws Exception { + return FluentIterable.from(query.get()) + .transform(new Function<ChangeInfo, Integer>() { + @Override + public Integer apply(ChangeInfo in) { + return in._number; + } + }).toList(); + } + + protected static Iterable<Integer> ids(Change... changes) { + return FluentIterable.from(Arrays.asList(changes)).transform( + new Function<Change, Integer>() { + @Override + public Integer apply(Change in) { + return in.getId().get(); + } + }); } protected static long lastUpdatedMs(Change c) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java new file mode 100644 index 0000000..ca1e2b1 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.query.change; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.testutil.TestChanges; + +import org.junit.Test; + +public class ChangeDataTest { + @Test + public void setPatchSetsClearsCurrentPatchSet() throws Exception { + ChangeData cd = ChangeData.createForTest(new Change.Id(1), 1); + cd.setChange(TestChanges.newChange( + new Project.NameKey("project"), new Account.Id(1000))); + PatchSet curr1 = cd.currentPatchSet(); + int currId = curr1.getId().get(); + PatchSet ps1 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 1)); + PatchSet ps2 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 2)); + cd.setPatchSets(ImmutableList.of(ps1, ps2)); + PatchSet curr2 = cd.currentPatchSet(); + assertThat(curr2).isNotSameAs(curr1); + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java index 742c230..6122d65 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
@@ -14,14 +14,12 @@ package com.google.gerrit.server.query.change; -import static org.junit.Assert.assertTrue; - import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.testutil.InMemoryModule; +import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo; import com.google.inject.Guice; import com.google.inject.Injector; -import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.revwalk.RevCommit; @@ -37,7 +35,7 @@ @Test public void fullTextWithSpecialChars() throws Exception { - TestRepository<InMemoryRepository> repo = createProject("repo"); + TestRepository<Repo> repo = createProject("repo"); RevCommit commit1 = repo.parseBody(repo.commit().message("foo_bar_foo").create()); Change change1 = newChange(repo, commit1, null, null, null).insert(); @@ -45,12 +43,12 @@ repo.parseBody(repo.commit().message("one.two.three").create()); Change change2 = newChange(repo, commit2, null, null, null).insert(); - assertTrue(query("message:foo_ba").isEmpty()); - assertResultEquals(change1, queryOne("message:bar")); - assertResultEquals(change1, queryOne("message:foo_bar")); - assertResultEquals(change1, queryOne("message:foo bar")); - assertResultEquals(change2, queryOne("message:two")); - assertResultEquals(change2, queryOne("message:one.two")); - assertResultEquals(change2, queryOne("message:one two")); + assertQuery("message:foo_ba"); + assertQuery("message:bar", change1); + assertQuery("message:foo_bar", change1); + assertQuery("message:foo bar", change1); + assertQuery("message:two", change2); + assertQuery("message:one.two", change2); + assertQuery("message:one two", change2); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java new file mode 100644 index 0000000..0feb800 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.query.change; + +import com.google.gerrit.testutil.InMemoryModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +import org.eclipse.jgit.lib.Config; +import org.junit.Ignore; +import org.junit.Test; + +public class LuceneQueryChangesV14Test extends LuceneQueryChangesTest { + @Override + protected Injector createInjector() { + Config luceneConfig = new Config(config); + InMemoryModule.setDefaults(luceneConfig); + // Latest version with a Lucene 4 index. + luceneConfig.setInt("index", "lucene", "testVersion", 14); + return Guice.createInjector(new InMemoryModule(luceneConfig)); + } + + @Override + @Ignore + @Test + public void byCommentBy() { + // Ignore. + } + + @Override + @Ignore + @Test + public void byFrom() { + // Ignore. + } + + @Override + @Ignore + @Test + public void byTopic() { + // Ignore. + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java index 0c8157d..d4398cd 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -25,6 +25,7 @@ import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.common.data.LabelValue; +import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.ProjectConfig; @@ -56,13 +57,20 @@ @Inject private InMemoryDatabase db; + private LifecycleManager lifecycle; + @Before public void setUp() throws Exception { + lifecycle = new LifecycleManager(); new InMemoryModule().inject(this); + lifecycle.start(); } @After public void tearDown() throws Exception { + if (lifecycle != null) { + lifecycle.stop(); + } InMemoryDatabase.drop(db); }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java index 8686fe6..7412b3f 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertEquals; +import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.reviewdb.client.SystemConfig; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.GerritPersonIdent; @@ -43,22 +44,29 @@ import org.junit.Before; import org.junit.Test; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.UUID; public class SchemaUpdaterTest { + private LifecycleManager lifecycle; private InMemoryDatabase db; @Before public void setUp() throws Exception { - db = InMemoryDatabase.newDatabase(); + lifecycle = new LifecycleManager(); + db = InMemoryDatabase.newDatabase(lifecycle); + lifecycle.start(); } @After public void tearDown() throws Exception { + if (lifecycle != null) { + lifecycle.stop(); + } InMemoryDatabase.drop(db); } @@ -67,7 +75,7 @@ IOException { db.create(); - final File site = new File(UUID.randomUUID().toString()); + final Path site = Paths.get(UUID.randomUUID().toString()); final SitePaths paths = new SitePaths(site); SchemaUpdater u = Guice.createInjector(new FactoryModule() { @Override @@ -129,6 +137,6 @@ db.assertSchemaVersion(); final SystemConfig sc = db.getSystemConfig(); - assertEquals(paths.site_path.getCanonicalPath(), sc.sitePath); + assertEquals(paths.site_path.toAbsolutePath().toString(), sc.sitePath); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java index d87888f..bd672f3 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/util/SubmoduleSectionParserTest.java
@@ -14,16 +14,19 @@ package com.google.gerrit.server.util; +import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.createStrictMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; +import com.google.common.collect.Sets; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.SubmoduleSubscription; -import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectState; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.lib.BlobBasedConfig; @@ -32,17 +35,15 @@ import org.junit.Test; import java.net.URI; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; public class SubmoduleSectionParserTest extends LocalDiskRepositoryTestCase { private static final String THIS_SERVER = "localhost"; - private GitRepositoryManager repoManager; + private ProjectCache projectCache; private BlobBasedConfig bbc; @Override @@ -50,16 +51,16 @@ public void setUp() throws Exception { super.setUp(); - repoManager = createStrictMock(GitRepositoryManager.class); + projectCache = createStrictMock(ProjectCache.class); bbc = createStrictMock(BlobBasedConfig.class); } private void doReplay() { - replay(repoManager, bbc); + replay(projectCache, bbc); } private void doVerify() { - verify(repoManager, bbc); + verify(projectCache, bbc); } @Test @@ -87,7 +88,7 @@ new Branch.NameKey(new Project.NameKey("super-project"), "refs/heads/master"); - List<SubmoduleSubscription> expectedSubscriptions = new ArrayList<>(); + Set<SubmoduleSubscription> expectedSubscriptions = Sets.newHashSet(); expectedSubscriptions .add(new SubmoduleSubscription(superBranchNameKey, new Branch.NameKey( new Project.NameKey("a"), "refs/heads/master"), "a")); @@ -134,7 +135,7 @@ new Branch.NameKey(new Project.NameKey("super-project"), "refs/heads/master"); - List<SubmoduleSubscription> expectedSubscriptions = new ArrayList<>(); + Set<SubmoduleSubscription> expectedSubscriptions = Sets.newHashSet(); expectedSubscriptions .add(new SubmoduleSubscription(superBranchNameKey, new Branch.NameKey( new Project.NameKey("a"), "refs/heads/master"), "a")); @@ -159,9 +160,10 @@ sectionsToReturn.put("a", new SubmoduleSection("ssh://review.source.com/a", "a", ".")); + Set<SubmoduleSubscription> expectedSubscriptions = Collections.emptySet(); execute(new Branch.NameKey(new Project.NameKey("super-project"), "refs/heads/master"), sectionsToReturn, new HashMap<String, String>(), - new ArrayList<SubmoduleSubscription>()); + expectedSubscriptions); } @Test @@ -170,9 +172,10 @@ sectionsToReturn.put("a", new SubmoduleSection("ssh://localhost/a", "a", ".")); + Set<SubmoduleSubscription> expectedSubscriptions = Collections.emptySet(); execute(new Branch.NameKey(new Project.NameKey("super-project"), "refs/heads/master"), sectionsToReturn, new HashMap<String, String>(), - new ArrayList<SubmoduleSubscription>()); + expectedSubscriptions); } @Test @@ -181,15 +184,16 @@ sectionsToReturn.put("project", new SubmoduleSection( "ssh://localhost/company/tools/project", "project", ".")); + Set<SubmoduleSubscription> expectedSubscriptions = Collections.emptySet(); execute(new Branch.NameKey(new Project.NameKey("super-project"), "refs/heads/master"), sectionsToReturn, new HashMap<String, String>(), - new ArrayList<SubmoduleSubscription>()); + expectedSubscriptions); } private void execute(final Branch.NameKey superProjectBranch, final Map<String, SubmoduleSection> sectionsToReturn, final Map<String, String> reposToBeFound, - final List<SubmoduleSubscription> expectedSubscriptions) throws Exception { + final Set<SubmoduleSubscription> expectedSubscriptions) throws Exception { expect(bbc.getSubsections("submodule")) .andReturn(sectionsToReturn.keySet()); @@ -214,13 +218,12 @@ projectNameCandidate.length() - Constants.DOT_GIT_EXT.length()); } if (projectNameCandidate.equals(reposToBeFound.get(id))) { - expect(repoManager.list()).andReturn( - new TreeSet<>(Collections.singletonList( - new Project.NameKey(projectNameCandidate)))); + expect(projectCache.get(new Project.NameKey(projectNameCandidate))) + .andReturn(createNiceMock(ProjectState.class)); break; } else { - expect(repoManager.list()).andReturn( - new TreeSet<>(Collections.<Project.NameKey> emptyList())); + expect(projectCache.get(new Project.NameKey(projectNameCandidate))) + .andReturn(null); } } } @@ -229,10 +232,10 @@ doReplay(); final SubmoduleSectionParser ssp = - new SubmoduleSectionParser(bbc, THIS_SERVER, superProjectBranch, - repoManager); + new SubmoduleSectionParser(projectCache, bbc, THIS_SERVER, + superProjectBranch); - List<SubmoduleSubscription> returnedSubscriptions = ssp.parseAllSections(); + Set<SubmoduleSubscription> returnedSubscriptions = ssp.parseAllSections(); doVerify();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/ConfigSuite.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/ConfigSuite.java index 707dd12..d769bcc 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/ConfigSuite.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/ConfigSuite.java
@@ -20,6 +20,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.google.common.base.MoreObjects; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.junit.runner.Runner; @@ -28,6 +29,7 @@ import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; +import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.reflect.Field; @@ -78,6 +80,9 @@ * * Additionally, config values used by <strong>default</strong> can be set * in a method annotated with {@code @ConfigSuite.Default}. + * <p> + * The name of the config method corresponding to the currently-running test can + * be stored in a field annotated with {@code @ConfigSuite.Name}. */ public class ConfigSuite extends Suite { private static final String DEFAULT = "default"; @@ -97,15 +102,22 @@ public static @interface Parameter { } + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface Name { + } + private static class ConfigRunner extends BlockJUnit4ClassRunner { private final Method configMethod; private final Field parameterField; + private final Field nameField; private final String name; - private ConfigRunner(Class<?> clazz, Field parameterField, String name, - Method configMethod) throws InitializationError { + private ConfigRunner(Class<?> clazz, Field parameterField, Field nameField, + String name, Method configMethod) throws InitializationError { super(clazz); this.parameterField = parameterField; + this.nameField = nameField; this.name = name; this.configMethod = configMethod; } @@ -114,6 +126,9 @@ public Object createTest() throws Exception { Object test = getTestClass().getJavaClass().newInstance(); parameterField.set(test, callConfigMethod(configMethod)); + if (nameField != null) { + nameField.set(test, name); + } return test; } @@ -132,15 +147,23 @@ private static List<Runner> runnersFor(Class<?> clazz) { Method defaultConfig = getDefaultConfig(clazz); List<Method> configs = getConfigs(clazz); - Field field = getParameterField(clazz); + Field parameterField = getOnlyField(clazz, Parameter.class); + checkArgument(parameterField != null, "No @ConfigSuite.Field found"); + Field nameField = getOnlyField(clazz, Name.class); List<Runner> result = Lists.newArrayListWithCapacity(configs.size() + 1); try { - result.add(new ConfigRunner(clazz, field, null, defaultConfig)); + result.add(new ConfigRunner( + clazz, parameterField, nameField, null, defaultConfig)); for (Method m : configs) { - result.add(new ConfigRunner(clazz, field, m.getName(), m)); + result.add(new ConfigRunner( + clazz, parameterField, nameField, m.getName(), m)); } return result; } catch (InitializationError e) { + System.err.println("Errors initializing runners:"); + for (Throwable t : e.getCauses()) { + t.printStackTrace(); + } throw new RuntimeException(e); } } @@ -191,16 +214,18 @@ } } - private static Field getParameterField(Class<?> clazz) { + private static Field getOnlyField(Class<?> clazz, + Class<? extends Annotation> ann) { List<Field> fields = Lists.newArrayListWithExpectedSize(1); for (Field f : clazz.getFields()) { - if (f.getAnnotation(Parameter.class) != null) { + if (f.getAnnotation(ann) != null) { fields.add(f); } } - checkArgument(fields.size() == 1, - "expected 1 @ConfigSuite.Parameter field, found: %s", fields); - return fields.get(0); + checkArgument(fields.size() <= 1, + "expected 1 @ConfigSuite.%s field, found: %s", + ann.getSimpleName(), fields); + return Iterables.getFirst(fields, null); } public ConfigSuite(Class<?> clazz) throws InitializationError {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeEmailSender.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeEmailSender.java new file mode 100644 index 0000000..7adf721 --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/FakeEmailSender.java
@@ -0,0 +1,134 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.testutil; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gerrit.common.errors.EmailException; +import com.google.gerrit.server.git.WorkQueue; +import com.google.gerrit.server.mail.Address; +import com.google.gerrit.server.mail.EmailHeader; +import com.google.gerrit.server.mail.EmailSender; +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +/** + * Email sender implementation that records messages in memory. + * <p> + * This class is mostly threadsafe. The only exception is that not all {@link + * EmailHeader} subclasses are immutable. In particular, if a caller holds a + * reference to an {@code AddressList} and mutates it after sending, the message + * returned by {@link #getMessages()} may or may not reflect mutations. + */ +@Singleton +public class FakeEmailSender implements EmailSender { + private static final Logger log = + LoggerFactory.getLogger(FakeEmailSender.class); + + public static class Module extends AbstractModule { + @Override + public void configure() { + bind(EmailSender.class).to(FakeEmailSender.class); + } + } + + @AutoValue + public abstract static class Message { + private static Message create(Address from, Collection<Address> rcpt, + Map<String, EmailHeader> headers, String body) { + return new AutoValue_FakeEmailSender_Message(from, + ImmutableList.copyOf(rcpt), ImmutableMap.copyOf(headers), body); + } + + public abstract Address from(); + public abstract ImmutableList<Address> rcpt(); + public abstract ImmutableMap<String, EmailHeader> headers(); + public abstract String body(); + } + + private final WorkQueue workQueue; + private final List<Message> messages; + + @Inject + FakeEmailSender(WorkQueue workQueue) { + this.workQueue = workQueue; + messages = Collections.synchronizedList(new ArrayList<Message>()); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean canEmail(String address) { + return true; + } + + @Override + public void send(Address from, Collection<Address> rcpt, + Map<String, EmailHeader> headers, String body) throws EmailException { + messages.add(Message.create(from, rcpt, headers, body)); + } + + public ImmutableList<Message> getMessages() { + waitForEmails(); + synchronized (messages) { + return ImmutableList.copyOf(messages); + } + } + + public ImmutableList<Message> getMessages(String changeId, String type) { + final String idFooter = "\nGerrit-Change-Id: " + changeId + "\n"; + final String typeFooter = "\nGerrit-MessageType: " + type + "\n"; + return FluentIterable.from(getMessages()) + .filter(new Predicate<Message>() { + @Override + public boolean apply(Message in) { + return in.body().contains(idFooter) + && in.body().contains(typeFooter); + } + }).toList(); + } + + private void waitForEmails() { + // TODO(dborowitz): This is brittle; consider forcing emails to use + // a single thread in tests (tricky because most callers just use the + // default executor). + for (WorkQueue.Task<?> task : workQueue.getTasks()) { + if (task.toString().contains("send-email")) { + try { + task.get(); + } catch (ExecutionException | InterruptedException e) { + log.warn("error finishing email task", e); + } + } + } + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java index 49fcc96..1f5b6cd 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertEquals; +import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.reviewdb.client.CurrentSchemaVersion; import com.google.gerrit.reviewdb.client.SystemConfig; import com.google.gerrit.reviewdb.server.ReviewDb; @@ -27,6 +28,7 @@ import com.google.gwtorm.server.SchemaFactory; import com.google.inject.Guice; import com.google.inject.Inject; +import com.google.inject.Injector; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -46,9 +48,10 @@ * the JVM running the unit tests doesn't run out of heap space. */ public class InMemoryDatabase implements SchemaFactory<ReviewDb> { - public static InMemoryDatabase newDatabase() { - return Guice.createInjector(new InMemoryModule()) - .getInstance(InMemoryDatabase.class); + public static InMemoryDatabase newDatabase(LifecycleManager lifecycle) { + Injector injector = Guice.createInjector(new InMemoryModule()); + lifecycle.add(injector); + return injector.getInstance(InMemoryDatabase.class); } private static int dbCnt;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java index 76af6f1..2219d28 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.inject.Scopes.SINGLETON; -import com.google.common.net.InetAddresses; import com.google.common.util.concurrent.MoreExecutors; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.DisabledChangeHooks; @@ -25,7 +24,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdentProvider; -import com.google.gerrit.server.RemotePeer; import com.google.gerrit.server.cache.h2.DefaultCacheFactory; import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AllProjectsNameProvider; @@ -46,11 +44,9 @@ import com.google.gerrit.server.git.GarbageCollection; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.PerThreadRequestScope; -import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.index.ChangeSchemas; import com.google.gerrit.server.index.IndexModule.IndexType; import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier; -import com.google.gerrit.server.mail.SmtpEmailSender; import com.google.gerrit.server.patch.DiffExecutor; import com.google.gerrit.server.schema.DataSourceType; import com.google.gerrit.server.schema.SchemaCreator; @@ -73,11 +69,10 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.PersonIdent; -import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.concurrent.ExecutorService; public class InMemoryModule extends FactoryModule { @@ -93,12 +88,12 @@ cfg.setString("gerrit", null, "allProjects", "Test-Projects"); cfg.setString("user", null, "name", "Gerrit Code Review"); cfg.setString("user", null, "email", "gerrit@localhost"); - cfg.setBoolean("sendemail", null, "enable", false); cfg.setString("cache", null, "directory", null); cfg.setString("index", null, "type", "lucene"); cfg.setBoolean("index", "lucene", "testInmemory", true); cfg.setInt("index", "lucene", "testVersion", ChangeSchemas.getLatest().getVersion()); + cfg.setInt("sendemail", null, "threadPoolSize", 0); } private final Config cfg; @@ -117,6 +112,13 @@ @Override protected void configure() { + // Do NOT bind @RemotePeer, as it is bound in a child injector of + // ChangeMergeQueue (bound via GerritGlobalModule below), so there cannot be + // a binding in the parent injector. If you need @RemotePeer, you must bind + // it in a child injector of the one containing InMemoryModule. But unless + // you really need to test something request-scoped, you likely don't + // actually need it. + // For simplicity, don't create child injectors, just use this one to get a // few required modules. Injector cfgInjector = Guice.createInjector(new AbstractModule() { @@ -132,10 +134,9 @@ bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST); - bind(File.class).annotatedWith(SitePath.class).toInstance(new File(".")); + // TODO(dborowitz): Use jimfs. + bind(Path.class).annotatedWith(SitePath.class).toInstance(Paths.get(".")); bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg); - bind(SocketAddress.class).annotatedWith(RemotePeer.class).toInstance( - new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 1234)); bind(PersonIdent.class) .annotatedWith(GerritPersonIdent.class) .toProvider(GerritPersonIdentProvider.class); @@ -180,7 +181,7 @@ } }); install(new DefaultCacheFactory.Module()); - install(new SmtpEmailSender.Module()); + install(new FakeEmailSender.Module()); install(new SignedTokenEmailTokenVerifier.Module()); IndexType indexType = null; @@ -204,10 +205,8 @@ @Provides @Singleton @EmailReviewCommentsExecutor - public WorkQueue.Executor createEmailReviewCommentsExecutor( - @GerritServerConfig Config config, WorkQueue queues) { - int poolSize = config.getInt("sendemail", null, "threadPoolSize", 1); - return queues.createQueue(poolSize, "EmailReviewComments"); + public ExecutorService createEmailReviewCommentsExecutor() { + return MoreExecutors.newDirectExecutorService(); } @Provides
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java index 0635464..ec53b29 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
@@ -35,16 +35,22 @@ return new Repo(name); } - private static class Description extends DfsRepositoryDescription { + public static class Description extends DfsRepositoryDescription { + private final Project.NameKey name; private String desc; private Description(Project.NameKey name) { super(name.get()); + this.name = name; desc = "In-memory repository " + name.get(); } + + public Project.NameKey getProject() { + return name; + } } - private static class Repo extends InMemoryRepository { + public static class Repo extends InMemoryRepository { private Repo(Project.NameKey name) { super(new Description(name)); } @@ -58,13 +64,13 @@ private Map<String, Repo> repos = Maps.newHashMap(); @Override - public InMemoryRepository openRepository(Project.NameKey name) + public synchronized Repo openRepository(Project.NameKey name) throws RepositoryNotFoundException { return get(name); } @Override - public InMemoryRepository createRepository(Project.NameKey name) + public synchronized Repo createRepository(Project.NameKey name) throws RepositoryCaseMismatchException, RepositoryNotFoundException { Repo repo; try { @@ -80,13 +86,13 @@ } @Override - public InMemoryRepository openMetadataRepository(Project.NameKey name) - throws RepositoryNotFoundException { + public synchronized Repo openMetadataRepository( + Project.NameKey name) throws RepositoryNotFoundException { return openRepository(name); } @Override - public SortedSet<Project.NameKey> list() { + public synchronized SortedSet<Project.NameKey> list() { SortedSet<Project.NameKey> names = Sets.newTreeSet(); for (DfsRepository repo : repos.values()) { names.add(new Project.NameKey(repo.getDescription().getRepositoryName())); @@ -95,13 +101,14 @@ } @Override - public String getProjectDescription(Project.NameKey name) + public synchronized String getProjectDescription(Project.NameKey name) throws RepositoryNotFoundException { return get(name).getDescription().desc; } @Override - public void setProjectDescription(Project.NameKey name, String description) { + public synchronized void setProjectDescription(Project.NameKey name, + String description) { try { get(name).getDescription().desc = description; } catch (RepositoryNotFoundException e) { @@ -109,7 +116,8 @@ } } - private Repo get(Project.NameKey name) throws RepositoryNotFoundException { + private synchronized Repo get(Project.NameKey name) + throws RepositoryNotFoundException { Repo repo = repos.get(name.get().toLowerCase()); if (repo != null) { return repo;
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java index ddb86c3..0faa691 100644 --- a/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java +++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java
@@ -25,8 +25,8 @@ import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; class IndexVersionCheck implements LifecycleListener { @@ -34,8 +34,8 @@ SolrChangeIndex.CHANGES_OPEN, ChangeSchemas.getLatest().getVersion(), SolrChangeIndex.CHANGES_CLOSED, ChangeSchemas.getLatest().getVersion()); - public static File solrIndexConfig(SitePaths sitePaths) { - return new File(sitePaths.index_dir, "gerrit_index.config"); + public static Path solrIndexConfig(SitePaths sitePaths) { + return sitePaths.index_dir.resolve("gerrit_index.config"); } private final SitePaths sitePaths; @@ -48,9 +48,9 @@ @Override public void start() { // TODO Query schema version from a special meta-document - File file = solrIndexConfig(sitePaths); + Path path = solrIndexConfig(sitePaths); try { - FileBasedConfig cfg = new FileBasedConfig(file, FS.detect()); + FileBasedConfig cfg = new FileBasedConfig(path.toFile(), FS.detect()); cfg.load(); for (Map.Entry<String, Integer> e : SCHEMA_VERSIONS.entrySet()) { int schemaVersion = cfg.getInt("index", e.getKey(), "schemaVersion", 0); @@ -61,9 +61,9 @@ } } } catch (IOException e) { - throw new ProvisionException("unable to read " + file); + throw new ProvisionException("unable to read " + path); } catch (ConfigInvalidException e) { - throw new ProvisionException("invalid config file " + file); + throw new ProvisionException("invalid config file " + path); } } @@ -75,6 +75,6 @@ private final String upgrade() { return "\nRun reindex to rebuild the index:\n" + "$ java -jar gerrit.war reindex -d " - + sitePaths.site_path.getAbsolutePath(); + + sitePaths.site_path.toAbsolutePath(); } }
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java index 78f5265..b9e47954 100644 --- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java +++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
@@ -319,7 +319,7 @@ doc.addField(name, value); } } else { - throw QueryBuilder.badFieldType(type); + throw FieldType.badFieldType(type); } } @@ -327,7 +327,7 @@ public void markReady(boolean ready) throws IOException { // TODO Move the schema version information to a special meta-document FileBasedConfig cfg = new FileBasedConfig( - solrIndexConfig(sitePaths), + solrIndexConfig(sitePaths).toFile(), FS.detect()); for (Map.Entry<String, Integer> e : SCHEMA_VERSIONS.entrySet()) { cfg.setInt("index", e.getKey(), "schemaVersion",
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java index 38de6ee..0133e33 100644 --- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java +++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java
@@ -50,7 +50,6 @@ @Override protected void configure() { - bind(IndexConfig.class).toInstance(IndexConfig.createDefault()); install(new IndexModule(threads)); bind(ChangeIndex.class).to(SolrChangeIndex.class); listener().to(SolrChangeIndex.class); @@ -61,6 +60,12 @@ @Provides @Singleton + IndexConfig getIndexConfig(@GerritServerConfig Config cfg) { + return IndexConfig.fromConfig(cfg); + } + + @Provides + @Singleton public SolrChangeIndex getChangeIndex(@GerritServerConfig Config cfg, Provider<ReviewDb> db, ChangeData.Factory changeDataFactory,
diff --git a/gerrit-sshd/BUCK b/gerrit-sshd/BUCK index 4774cb3..7bee47e 100644 --- a/gerrit-sshd/BUCK +++ b/gerrit-sshd/BUCK
@@ -28,6 +28,7 @@ '//lib/mina:core', '//lib/mina:sshd', '//lib/jgit:jgit', + '//lib/jgit:jgit-archive', ], provided_deps = [ '//lib/bouncycastle:bcprov',
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java index c0fd2ac..c38394a 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -338,8 +338,7 @@ return 127; } - if (e instanceof UnloggedFailure) { - } else { + if (!(e instanceof UnloggedFailure)) { final StringBuilder m = new StringBuilder(); m.append("Internal server error"); if (userProvider.get().isIdentifiedUser()) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java index 56441fba..6bd9c4c 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -263,30 +263,33 @@ switch (b) { case '\t': case ' ': - if (inquote || inDblQuote) + if (inquote || inDblQuote) { r.append(b); - else if (r.length() > 0) { + } else if (r.length() > 0) { list.add(r.toString()); r = new StringBuilder(); } continue; case '\"': - if (inquote) + if (inquote) { r.append(b); - else + } else { inDblQuote = !inDblQuote; + } continue; case '\'': - if (inDblQuote) + if (inDblQuote) { r.append(b); - else + } else { inquote = !inquote; + } continue; case '\\': - if (inquote || ip == commandLine.length()) + if (inquote || ip == commandLine.length()) { r.append(b); // literal within a quote - else + } else { r.append(commandLine.charAt(ip++)); + } continue; default: r.append(b);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java index cc7b637..e453a4b 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
@@ -14,7 +14,10 @@ package com.google.gerrit.sshd; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.common.base.Preconditions; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.reviewdb.client.AccountSshKey; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.PeerDaemonUser; @@ -33,10 +36,10 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.security.KeyPair; import java.security.PublicKey; import java.util.Collection; @@ -65,7 +68,7 @@ DatabasePubKeyAuth(final SshKeyCacheImpl skc, final SshLog l, final IdentifiedUser.GenericFactory uf, final PeerDaemonUser.Factory pf, final SitePaths site, final KeyPairProvider hostKeyProvider, - final @GerritServerConfig Config cfg, final SshScope s) { + @GerritServerConfig final Config cfg, final SshScope s) { sshKeyCache = skc; sshLog = l; userFactory = uf; @@ -169,56 +172,50 @@ } private static class PeerKeyCache { - private final File path; + private final Path path; private final long modified; final Set<PublicKey> keys; - PeerKeyCache(final File path) { + PeerKeyCache(Path path) { this.path = path; - this.modified = path.lastModified(); + this.modified = FileUtil.lastModified(path); this.keys = read(path); } - private static Set<PublicKey> read(File path) { - try { - final BufferedReader br = new BufferedReader(new FileReader(path)); - try { - final Set<PublicKey> keys = new HashSet<>(); - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - - try { - byte[] bin = Base64.decodeBase64(line.getBytes("ISO-8859-1")); - keys.add(new Buffer(bin).getRawPublicKey()); - } catch (RuntimeException e) { - logBadKey(path, line, e); - } catch (SshException e) { - logBadKey(path, line, e); - } + private static Set<PublicKey> read(Path path) { + try (BufferedReader br = Files.newBufferedReader(path, UTF_8)) { + final Set<PublicKey> keys = new HashSet<>(); + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.startsWith("#") || line.isEmpty()) { + continue; } - return Collections.unmodifiableSet(keys); - } finally { - br.close(); - } - } catch (FileNotFoundException noFile) { - return Collections.emptySet(); + try { + byte[] bin = Base64.decodeBase64(line.getBytes("ISO-8859-1")); + keys.add(new Buffer(bin).getRawPublicKey()); + } catch (RuntimeException e) { + logBadKey(path, line, e); + } catch (SshException e) { + logBadKey(path, line, e); + } + } + return Collections.unmodifiableSet(keys); + } catch (NoSuchFileException noFile) { + return Collections.emptySet(); } catch (IOException err) { log.error("Cannot read " + path, err); return Collections.emptySet(); } } - private static void logBadKey(File path, String line, Exception e) { + private static void logBadKey(Path path, String line, Exception e) { log.warn("Invalid key in " + path + ":\n " + line, e); } boolean isCurrent() { - return path.lastModified() == modified; + return modified == FileUtil.lastModified(path); } PeerKeyCache reload() {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java index a7748ff..09222b7 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -88,10 +88,11 @@ checkRequiresCapability(cmd); if (cmd instanceof BaseCommand) { final BaseCommand bc = (BaseCommand) cmd; - if (getName().isEmpty()) + if (getName().isEmpty()) { bc.setName(commandName); - else + } else { bc.setName(getName() + " " + commandName); + } bc.setArguments(args.toArray(new String[args.size()])); } else if (!args.isEmpty()) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java index 241f853..3e6e2f5 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java
@@ -24,7 +24,8 @@ import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -38,29 +39,29 @@ @Override public KeyPairProvider get() { - final File objKey = site.ssh_key; - final File rsaKey = site.ssh_rsa; - final File dsaKey = site.ssh_dsa; + Path objKey = site.ssh_key; + Path rsaKey = site.ssh_rsa; + Path dsaKey = site.ssh_dsa; final List<String> stdKeys = new ArrayList<>(2); - if (rsaKey.exists()) { - stdKeys.add(rsaKey.getAbsolutePath()); + if (Files.exists(rsaKey)) { + stdKeys.add(rsaKey.toAbsolutePath().toString()); } - if (dsaKey.exists()) { - stdKeys.add(dsaKey.getAbsolutePath()); + if (Files.exists(dsaKey)) { + stdKeys.add(dsaKey.toAbsolutePath().toString()); } - if (objKey.exists()) { + if (Files.exists(objKey)) { if (stdKeys.isEmpty()) { SimpleGeneratorHostKeyProvider p = new SimpleGeneratorHostKeyProvider(); - p.setPath(objKey.getAbsolutePath()); + p.setPath(objKey.toAbsolutePath().toString()); return p; } else { // Both formats of host key exist, we don't know which format // should be authoritative. Complain and abort. // - stdKeys.add(objKey.getAbsolutePath()); + stdKeys.add(objKey.toAbsolutePath().toString()); throw new ProvisionException("Multiple host keys exist: " + stdKeys); }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java index 39eb720..33ffb47 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -135,12 +135,14 @@ * <p> * Versions of Git before 1.5.3 may require setting the username and port * properties in the user's {@code ~/.ssh/config} file, and using a host - * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git: + * alias through a URL such as {@code gerrit-alias:/tools/gerrit.git}: * <pre> + * {@code * Host gerrit-alias * User sop@google.com * Hostname gerrit.com * Port 8010 + * } * </pre> */ @Singleton @@ -443,7 +445,8 @@ if ((n & -n) == n) { return (int)((n * (long) next(31)) >> 31); } - int bits, val; + int bits; + int val; do { bits = next(31); val = bits % n; @@ -453,9 +456,9 @@ throw new IllegalArgumentException(); } - final protected int next(int numBits) { + protected final int next(int numBits) { int bytes = (numBits+7)/8; - byte next[] = new byte[bytes]; + byte[] next = new byte[bytes]; int ret = 0; random.nextBytes(next); for (int i = 0; i < bytes; i++) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java index 439b8c8..4d6a790 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -220,7 +220,8 @@ event.setProperty(P_SESSION, id(sd.getSessionId())); - String userName = "-", accountId = "-"; + String userName = "-"; + String accountId = "-"; if (user != null && user.isIdentifiedUser()) { IdentifiedUser u = (IdentifiedUser) user;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java index 55f6158..b1d09e9 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
@@ -26,7 +26,7 @@ /** Create a new branch. **/ @CommandMetaData(name = "create-branch", description = "Create a new branch") -final public class CreateBranchCommand extends SshCommand { +public final class CreateBranchCommand extends SshCommand { @Argument(index = 0, required = true, metaVar = "PROJECT", usage = "name of the project") private ProjectControl project;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java index 75194dc..155c2eb 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -14,17 +14,22 @@ package com.google.gerrit.sshd.commands; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; import com.google.gerrit.common.data.GlobalCapability; -import com.google.gerrit.common.errors.NameAlreadyUsedException; -import com.google.gerrit.common.errors.PermissionDeniedException; import com.google.gerrit.extensions.annotations.RequiresCapability; -import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.extensions.api.groups.GroupInput; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.server.account.CreateGroupArgs; -import com.google.gerrit.server.account.PerformCreateGroup; -import com.google.gerrit.server.validators.GroupCreationValidationListener; -import com.google.gerrit.server.validators.ValidationException; +import com.google.gerrit.server.group.AddIncludedGroups; +import com.google.gerrit.server.group.AddMembers; +import com.google.gerrit.server.group.CreateGroup; +import com.google.gerrit.server.group.GroupResource; +import com.google.gerrit.server.group.GroupsCollection; import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.SshCommand; import com.google.gwtorm.server.OrmException; @@ -71,36 +76,75 @@ } @Inject - private PerformCreateGroup.Factory performCreateGroupFactory; + private CreateGroup.Factory createGroupFactory; @Inject - private DynamicSet<GroupCreationValidationListener> groupCreationValidationListeners; + private GroupsCollection groups; + + @Inject + private AddMembers addMembers; + + @Inject + private AddIncludedGroups addIncludedGroups; @Override protected void run() throws Failure, OrmException { try { - CreateGroupArgs args = new CreateGroupArgs(); - args.setGroupName(groupName); - args.groupDescription = groupDescription; - args.visibleToAll = visibleToAll; - args.ownerGroupId = ownerGroupId; - args.initialMembers = initialMembers; - args.initialGroups = initialGroups; + GroupResource rsrc = createGroup(); - for (GroupCreationValidationListener l : groupCreationValidationListeners) { - try { - l.validateNewGroup(args); - } catch (ValidationException e) { - die(e); - } + if (!initialMembers.isEmpty()) { + addMembers(rsrc); } - performCreateGroupFactory.create(args).createGroup(); - } catch (PermissionDeniedException e) { - throw die(e); - - } catch (NameAlreadyUsedException e) { + if (!initialGroups.isEmpty()) { + addIncludedGroups(rsrc); + } + } catch (RestApiException e) { throw die(e); } } + + private GroupResource createGroup() throws RestApiException, OrmException { + GroupInput input = new GroupInput(); + input.description = groupDescription; + input.visibleToAll = visibleToAll; + + if (ownerGroupId != null) { + input.ownerId = String.valueOf(ownerGroupId.get()); + } + + GroupInfo group = createGroupFactory.create(groupName) + .apply(TopLevelResource.INSTANCE, input); + return groups.parse(TopLevelResource.INSTANCE, + IdString.fromUrl(group.id)); + } + + private void addMembers(GroupResource rsrc) throws RestApiException, + OrmException { + AddMembers.Input input = + AddMembers.Input.fromMembers(FluentIterable + .from(initialMembers) + .transform(new Function<Account.Id, String>() { + @Override + public String apply(Account.Id id) { + return String.valueOf(id.get()); + } + }) + .toList()); + addMembers.apply(rsrc, input); + } + + private void addIncludedGroups(GroupResource rsrc) throws RestApiException, + OrmException { + AddIncludedGroups.Input input = + AddIncludedGroups.Input.fromGroups(FluentIterable.from(initialGroups) + .transform(new Function<AccountGroup.UUID, String>() { + @Override + public String apply(AccountGroup.UUID id) { + return id.get(); + } + }).toList()); + + addIncludedGroups.apply(rsrc, input); + } }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java index 4e151b3..7dc558e 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -109,7 +109,8 @@ requireChangeID = InheritableBoolean.TRUE; } - @Option(name = "--create-new-change-for-all-not-in-target", aliases = {"--ncfa"}, usage = "if a new change will be created for every commit not in target branch") + @Option(name = "--create-new-change-for-all-not-in-target", aliases = {"--ncfa"}, + usage = "if a new change will be created for every commit not in target branch") void setNewChangeForAllNotInTarget(@SuppressWarnings("unused") boolean on) { createNewChangeForAllNotInTarget = InheritableBoolean.TRUE; } @@ -124,7 +125,8 @@ @Option(name = "--max-object-size-limit", usage = "max Git object size for this project") private String maxObjectSizeLimit; - @Option(name = "--plugin-config", usage = "plugin configuration parameter with format '<plugin-name>.<parameter-name>=<value>'") + @Option(name = "--plugin-config", + usage = "plugin configuration parameter with format '<plugin-name>.<parameter-name>=<value>'") private List<String> pluginConfigValues; private String projectName; @@ -181,7 +183,7 @@ input.pluginConfigValues = parsePluginConfigValues(pluginConfigValues); } - gApi.projects().name(projectName).create(input); + gApi.projects().create(input); } else { List<Project.NameKey> parentCandidates = suggestParentCandidates.getNameKeys();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java index f75eb2b..2d071c7 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -70,15 +70,23 @@ // Honor the legacy hyphenated forms as aliases for the non-hyphenated forms command("git-upload-pack").to(Commands.key(git, "upload-pack")); command(git, "upload-pack").to(Upload.class); + command("git-upload-archive").to(Commands.key(git, "upload-archive")); + command(git, "upload-archive").to(UploadArchive.class); command("suexec").to(SuExec.class); listener().to(ShowCaches.StartupListener.class); - // The following commands can only be ran on a server in Master mode + // The following commands can only be run on a server in Master mode command(gerrit, CreateAccountCommand.class); command(gerrit, CreateGroupCommand.class); command(gerrit, CreateProjectCommand.class); + command(gerrit, SetHeadCommand.class); command(gerrit, AdminQueryShell.class); - if (!slaveMode) { + if (slaveMode) { + command("git-receive-pack").to(NotSupportedInSlaveModeFailureCommand.class); + command("gerrit-receive-pack").to(NotSupportedInSlaveModeFailureCommand.class); + command(git, "receive-pack").to(NotSupportedInSlaveModeFailureCommand.class); + command(gerrit, "test-submit").to(NotSupportedInSlaveModeFailureCommand.class); + } else { command("git-receive-pack").to(Commands.key(git, "receive-pack")); command("gerrit-receive-pack").to(Commands.key(git, "receive-pack")); command(git, "receive-pack").to(Commands.key(gerrit, "receive-pack"));
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java index c533f4f..d02fb4c 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -46,6 +46,9 @@ @Option(name = "--show-progress", usage = "progress information is shown") private boolean showProgress; + @Option(name = "--aggressive", usage = "run aggressive garbage collection") + private boolean aggressive; + @Argument(index = 0, required = false, multiValued = true, metaVar = "NAME", usage = "projects for which the Git garbage collection should be run") private List<ProjectControl> projects = new ArrayList<>(); @@ -85,7 +88,8 @@ } GarbageCollectionResult result = - garbageCollectionFactory.create().run(projectNames, showProgress ? stdout : null); + garbageCollectionFactory.create().run(projectNames, aggressive, + showProgress ? stdout : null); if (result.hasErrors()) { for (GarbageCollectionResult.Error e : result.getErrors()) { String msg;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java index 82ad16f..ad72b13 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -18,6 +18,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; +import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.IdentifiedUser; @@ -25,7 +26,6 @@ import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.group.GroupJson; -import com.google.gerrit.server.group.GroupJson.GroupInfo; import com.google.gerrit.server.group.ListGroups; import com.google.gerrit.server.ioutil.ColumnFormatter; import com.google.gerrit.sshd.CommandMetaData;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java index c41fcdc..15e030f 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -20,7 +20,7 @@ import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountResolver; @@ -110,7 +110,7 @@ db, true).filter(repo.getRefDatabase().getRefs(ALL), false); for (final String ref : refsMap.keySet()) { - if (!onlyRefsHeads || ref.startsWith(Branch.R_HEADS)) { + if (!onlyRefsHeads || ref.startsWith(RefNames.REFS_HEADS)) { stdout.println(ref); } }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/NotSupportedInSlaveModeFailureCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/NotSupportedInSlaveModeFailureCommand.java new file mode 100644 index 0000000..39e6d4a --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/NotSupportedInSlaveModeFailureCommand.java
@@ -0,0 +1,25 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.sshd.SshCommand; + +/* Failure command, that produces verbose failure message in slave mode */ +public class NotSupportedInSlaveModeFailureCommand extends SshCommand { + @Override + protected void run() throws UnloggedFailure { + throw die(getName() + ": is not supported in slave mode"); + } +}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java index 8f26bba..6a71176 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
@@ -758,7 +758,9 @@ r.append(" ("); boolean first = true; for (String c : columns.values()) { - if (!first) r.append(", "); + if (!first) { + r.append(", "); + } r.append(c); first = false; }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java index 38535a4..c8ebb6c 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -14,10 +14,13 @@ package com.google.gerrit.sshd.commands; -import com.google.gerrit.common.errors.InvalidNameException; -import com.google.gerrit.common.errors.NameAlreadyUsedException; import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.server.account.PerformRenameGroup; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.server.group.GroupResource; +import com.google.gerrit.server.group.GroupsCollection; +import com.google.gerrit.server.group.PutName; import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.SshCommand; import com.google.gwtorm.server.OrmException; @@ -34,19 +37,20 @@ private String newGroupName; @Inject - private PerformRenameGroup.Factory performRenameGroupFactory; + private GroupsCollection groups; + + @Inject + private PutName putName; @Override protected void run() throws Failure { try { - performRenameGroupFactory.create().renameGroup(groupName, newGroupName); - } catch (OrmException e) { - throw die(e); - } catch (InvalidNameException e) { - throw die(e); - } catch (NameAlreadyUsedException e) { - throw die(e); - } catch (NoSuchGroupException e) { + GroupResource rsrc = groups.parse(TopLevelResource.INSTANCE, + IdString.fromDecoded(groupName)); + PutName.Input input = new PutName.Input(); + input.name = newGroupName; + putName.apply(rsrc, input); + } catch (RestApiException | OrmException | NoSuchGroupException e) { throw die(e); } }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java index 3ac72a7..bc820a4 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -73,7 +73,9 @@ private final Set<PatchSet> patchSets = new HashSet<>(); - @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "list of commits or patch sets to review") + @Argument(index = 0, required = true, multiValued = true, + metaVar = "{COMMIT | CHANGE,PATCHSET}", + usage = "list of commits or patch sets to review") void addPatchSetId(final String token) { try { PatchSet ps = CommandUtils.parsePatchSet(token, db, projectControl,
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java new file mode 100644 index 0000000..b1d1605 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.project.SetHead; +import com.google.gerrit.server.project.SetHead.Input; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@CommandMetaData(name = "set-head", description = "Change HEAD reference for a project") +public class SetHeadCommand extends SshCommand { + + @Argument(index = 0, required = true, metaVar = "NAME", usage = "name of the project") + private ProjectControl project; + + @Option(name = "--new-head", required = true, metaVar = "REF", usage = "new HEAD reference") + private String newHead; + + private final SetHead setHead; + + @Inject + SetHeadCommand(SetHead setHead) { + this.setHead = setHead; + } + + @Override + protected void run() throws Exception { + Input input = new SetHead.Input(); + input.ref = newHead; + try { + setHead.apply(new ProjectResource(project), input); + } catch (UnprocessableEntityException e) { + throw new UnloggedFailure("fatal: " + e.getMessage()); + } + } +}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java new file mode 100644 index 0000000..929f7ea --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -0,0 +1,226 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.change.ArchiveFormat; +import com.google.gerrit.server.change.GetArchive; +import com.google.gerrit.sshd.AbstractGitCommand; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import org.eclipse.jgit.api.ArchiveCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.PacketLineIn; +import org.eclipse.jgit.transport.PacketLineOut; +import org.eclipse.jgit.transport.SideBandOutputStream; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Allows getting archives for Git repositories over SSH using the Git + * upload-archive protocol. + */ +public class UploadArchive extends AbstractGitCommand { + /** + * Options for parsing Git commands. + * <p> + * These options are not passed on command line, but received through input + * stream in pkt-line format. + */ + static class Options { + @Option(name = "-f", aliases = {"--format"}, usage = "Format of the" + + " resulting archive: tar or zip... If this option is not given, and" + + " the output file is specified, the format is inferred from the" + + " filename if possible (e.g. writing to \"foo.zip\" makes the output" + + " to be in the zip format). Otherwise the output format is tar.") + private String format = "tar"; + + @Option(name = "--prefix", + usage = "Prepend <prefix>/ to each filename in the archive.") + private String prefix; + + @Option(name = "-0", usage = "Store the files instead of deflating them.") + private boolean level0; + @Option(name = "-1") + private boolean level1; + @Option(name = "-2") + private boolean level2; + @Option(name = "-3") + private boolean level3; + @Option(name = "-4") + private boolean level4; + @Option(name = "-5") + private boolean level5; + @Option(name = "-6") + private boolean level6; + @Option(name = "-7") + private boolean level7; + @Option(name = "-8") + private boolean level8; + @Option(name = "-9", usage = "Highest and slowest compression level. You " + + "can specify any number from 1 to 9 to adjust compression speed and " + + "ratio.") + private boolean level9; + + @Argument(index = 0, required = true, usage = "The tree or commit to " + + "produce an archive for.") + private String treeIsh = "master"; + + @Argument(index = 1, multiValued = true, usage = + "Without an optional path parameter, all files and subdirectories of " + + "the current working directory are included in the archive. If one " + + "or more paths are specified, only these are included.") + private List<String> path; + } + + @Inject + private GetArchive.AllowedFormats allowedFormats; + @Inject + private Provider<ReviewDb> db; + private Options options = new Options(); + + /** + * Read and parse arguments from input stream. + * This method gets the arguments from input stream, in Pkt-line format, + * then parses them to fill the options object. + */ + protected void readArguments() throws IOException, Failure { + String argCmd = "argument "; + List<String> args = Lists.newArrayList(); + + // Read arguments in Pkt-Line format + PacketLineIn packetIn = new PacketLineIn(in); + for (;;) { + String s = packetIn.readString(); + if (s == PacketLineIn.END) { + break; + } + if (!s.startsWith(argCmd)) { + throw new Failure(1, "fatal: 'argument' token or flush expected"); + } + String[] parts = s.substring(argCmd.length()).split("=", 2); + for(String p : parts) { + args.add(p); + } + } + + try { + // Parse them into the 'options' field + CmdLineParser parser = new CmdLineParser(options); + parser.parseArgument(args); + if (options.path == null || Arrays.asList(".").equals(options.path)) { + options.path = Collections.emptyList(); + } + } catch (CmdLineException e) { + throw new Failure(2, "fatal: unable to parse arguments, " + e); + } + } + + @Override + protected void runImpl() throws IOException, Failure { + PacketLineOut packetOut = new PacketLineOut(out); + packetOut.setFlushOnEnd(true); + packetOut.writeString("ACK"); + packetOut.end(); + + try { + // Parse Git arguments + readArguments(); + + ArchiveFormat f = allowedFormats.getExtensions().get("." + options.format); + if (f == null) { + throw new Failure(3, "fatal: upload-archive not permitted"); + } + + // Find out the object to get from the specified reference and paths + ObjectId treeId = repo.resolve(options.treeIsh); + if (treeId.equals(ObjectId.zeroId())) { + throw new Failure(4, "fatal: reference not found"); + } + + // Verify the user has permissions to read the specified reference + if (!projectControl.allRefsAreVisible() && !canRead(treeId)) { + throw new Failure(5, "fatal: cannot perform upload-archive operation"); + } + + try { + // The archive is sent in DATA sideband channel + SideBandOutputStream sidebandOut = + new SideBandOutputStream(SideBandOutputStream.CH_DATA, + SideBandOutputStream.MAX_BUF, out); + new ArchiveCommand(repo) + .setFormat(f.name()) + .setFormatOptions(getFormatOptions(f)) + .setTree(treeId) + .setPaths(options.path.toArray(new String[0])) + .setPrefix(options.prefix) + .setOutputStream(sidebandOut) + .call(); + sidebandOut.flush(); + sidebandOut.close(); + } catch (GitAPIException e) { + throw new Failure(7, "fatal: git api exception, " + e); + } + } catch (Failure f) { + // Report the error in ERROR sideband channel + SideBandOutputStream sidebandError = + new SideBandOutputStream(SideBandOutputStream.CH_ERROR, + SideBandOutputStream.MAX_BUF, out); + sidebandError.write(f.getMessage().getBytes(UTF_8)); + sidebandError.flush(); + sidebandError.close(); + throw f; + } finally { + // In any case, cleanly close the packetOut channel + packetOut.end(); + } + } + + private Map<String, Object> getFormatOptions(ArchiveFormat f) { + if (f == ArchiveFormat.ZIP) { + int value = Arrays.asList(options.level0, options.level1, options.level2, + options.level3, options.level4, options.level5, options.level6, + options.level7, options.level8, options.level9).indexOf(true); + if (value >= 0) { + return ImmutableMap.<String, Object> of( + "level", Integer.valueOf(value)); + } + } + return Collections.emptyMap(); + } + + private boolean canRead(ObjectId revId) throws IOException { + try (RevWalk rw = new RevWalk(repo)) { + RevCommit commit = rw.parseCommit(revId); + return projectControl.canReadCommit(db.get(), rw, commit); + } + } +}
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java index 734b7045..e70ab72 100644 --- a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java +++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -202,8 +202,9 @@ for (int argi = 0; argi < args.length; argi++) { final String str = args[argi]; if (str.equals("--")) { - while (argi < args.length) + while (argi < args.length) { tmp.add(args[argi++]); + } break; }
diff --git a/gerrit-util-http/src/main/java/com/google/gerrit/util/http/RequestUtil.java b/gerrit-util-http/src/main/java/com/google/gerrit/util/http/RequestUtil.java index 922a8d5..888ad2f 100644 --- a/gerrit-util-http/src/main/java/com/google/gerrit/util/http/RequestUtil.java +++ b/gerrit-util-http/src/main/java/com/google/gerrit/util/http/RequestUtil.java
@@ -35,6 +35,7 @@ * without decoding URL-encoded characters. */ public static String getEncodedPathInfo(HttpServletRequest req) { + // CS IGNORE LineLength FOR NEXT 3 LINES. REASON: URL. // Based on com.google.guice.ServletDefinition$1#getPathInfo() from: // https://github.com/google/guice/blob/41c126f99d6309886a0ded2ac729033d755e1593/extensions/servlet/src/com/google/inject/servlet/ServletDefinition.java String servletPath = req.getServletPath();
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml index 0fb1c9a..17e8286 100644 --- a/gerrit-war/pom.xml +++ b/gerrit-war/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-war</artifactId> - <version>2.11.1</version> + <version>2.12-SNAPSHOT</version> <packaging>war</packaging> <name>Gerrit Code Review - WAR</name> <description>Gerrit WAR</description>
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java index 6bbbd8f..ea4a3ea 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java
@@ -20,7 +20,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -47,21 +48,19 @@ public void init() { try { if (sitePath != null) { - File site = new File(sitePath); - LOG.info(String.format("Initializing site at %s", - site.getAbsolutePath())); + Path site = Paths.get(sitePath); + LOG.info("Initializing site at " + site.toRealPath().normalize()); new BaseInit(site, false, true, pluginsDistribution, pluginsToInstall).run(); return; } try (Connection conn = connectToDb()) { - File site = getSiteFromReviewDb(conn); + Path site = getSiteFromReviewDb(conn); if (site == null && initPath != null) { - site = new File(initPath); + site = Paths.get(initPath); } if (site != null) { - LOG.info(String.format("Initializing site at %s", - site.getAbsolutePath())); + LOG.info("Initializing site at " + site.toRealPath().normalize()); new BaseInit(site, new ReviewDbDataSourceProvider(), false, false, pluginsDistribution, pluginsToInstall).run(); } @@ -76,12 +75,12 @@ return new ReviewDbDataSourceProvider().get().getConnection(); } - private File getSiteFromReviewDb(Connection conn) { + private Path getSiteFromReviewDb(Connection conn) { try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT site_path FROM system_config")) { if (rs.next()) { - return new File(rs.getString(1)); + return Paths.get(rs.getString(1)); } } catch (SQLException e) { return null;
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java index b97df3f..60f389e 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
@@ -22,12 +22,13 @@ import com.google.inject.Inject; import com.google.inject.Provider; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; -/** Provides {@link java.io.File} annotated with {@link SitePath}. */ -class SitePathFromSystemConfigProvider implements Provider<File> { - private final File path; +/** Provides {@link Path} annotated with {@link SitePath}. */ +class SitePathFromSystemConfigProvider implements Provider<Path> { + private final Path path; @Inject SitePathFromSystemConfigProvider(SchemaFactory<ReviewDb> schemaFactory) @@ -36,18 +37,18 @@ } @Override - public File get() { + public Path get() { return path; } - private static File read(SchemaFactory<ReviewDb> schemaFactory) + private static Path read(SchemaFactory<ReviewDb> schemaFactory) throws OrmException { ReviewDb db = schemaFactory.open(); try { List<SystemConfig> all = db.systemConfig().all().toList(); switch (all.size()) { case 1: - return new File(all.get(0).sitePath); + return Paths.get(all.get(0).sitePath); case 0: throw new OrmException("system_config table is empty"); default:
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java index b365e76..3bc8b58 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -82,8 +82,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -105,7 +106,7 @@ private static final Logger log = LoggerFactory.getLogger(WebAppInitializer.class); - private File sitePath; + private Path sitePath; private Injector dbInjector; private Injector cfgInjector; private Injector sysInjector; @@ -126,7 +127,7 @@ if (manager == null) { final String path = System.getProperty("gerrit.site_path"); if (path != null) { - sitePath = new File(path); + sitePath = Paths.get(path); } if (System.getProperty("gerrit.init") != null) { @@ -215,7 +216,7 @@ Module sitePathModule = new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath); } }; modules.add(sitePathModule); @@ -267,7 +268,7 @@ modules.add(new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toProvider( + bind(Path.class).annotatedWith(SitePath.class).toProvider( SitePathFromSystemConfigProvider.class).in(SINGLETON); } });
diff --git a/lib/BUCK b/lib/BUCK index a880f06..d91ea26 100644 --- a/lib/BUCK +++ b/lib/BUCK
@@ -45,8 +45,8 @@ maven_jar( name = 'gson', - id = 'com.google.code.gson:gson:2.1', - sha1 = '2e66da15851f9f5b5079228f856c2f090ba98c38', + id = 'com.google.code.gson:gson:2.3.1', + sha1 = 'ecb6e1f8e4b0e84c4b886c2f14a1500caf309757', license = 'Apache2.0', ) @@ -188,8 +188,8 @@ maven_jar( name = 'truth', - id = 'com.google.truth:truth:0.25', - sha1 = '503ba892e8482976b81eb2b2df292858fbac3782', + id = 'com.google.truth:truth:0.26', + sha1 = 'b5802815625d82f39c33219299771f3d64301b06', license = 'DO_NOT_DISTRIBUTE', deps = [ ':guava',
diff --git a/lib/asciidoctor/java/DocIndexer.java b/lib/asciidoctor/java/DocIndexer.java index 7eb70c1..f06c662 100644 --- a/lib/asciidoctor/java/DocIndexer.java +++ b/lib/asciidoctor/java/DocIndexer.java
@@ -25,7 +25,6 @@ import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.RAMDirectory; -import org.apache.lucene.util.Version; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; @@ -51,8 +50,6 @@ import java.util.zip.ZipOutputStream; public class DocIndexer { - @SuppressWarnings("deprecation") - private static final Version LUCENE_VERSION = Version.LUCENE_4_10_1; private static final Pattern SECTION_HEADER = Pattern.compile("^=+ (.*)"); @Option(name = "-o", usage = "output JAR file") @@ -99,9 +96,9 @@ UnsupportedEncodingException, FileNotFoundException { RAMDirectory directory = new RAMDirectory(); IndexWriterConfig config = new IndexWriterConfig( - LUCENE_VERSION, new StandardAnalyzer(CharArraySet.EMPTY_SET)); config.setOpenMode(OpenMode.CREATE); + config.setCommitOnClose(true); IndexWriter iwriter = new IndexWriter(directory, config); for (String inputFile : inputFiles) { File file = new File(inputFile);
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs index db87b25..8259252 100644 --- a/lib/codemirror/cm.defs +++ b/lib/codemirror/cm.defs
@@ -64,6 +64,7 @@ 'php', 'pig', 'properties', + 'puppet', 'python', 'r', 'rst',
diff --git a/lib/commons/BUCK b/lib/commons/BUCK index fe249fa..ca989c7 100644 --- a/lib/commons/BUCK +++ b/lib/commons/BUCK
@@ -23,7 +23,6 @@ sha1 = 'ab365c96ee9bc88adcc6fa40d185c8e15a31410d', license = 'Apache2.0', exclude = ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt'], - visibility = ['//lib/jgit:jgit-archive'], ) maven_jar( @@ -41,8 +40,8 @@ maven_jar( name = 'lang', - id = 'commons-lang:commons-lang:2.5', - sha1 = 'b0236b252e86419eef20c31a44579d2aee2f0a69', + id = 'commons-lang:commons-lang:2.6', + sha1 = '0ce1edb914c94ebc388f086c6827e8bdeec71ac2', license = 'Apache2.0', exclude = ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt'], )
diff --git a/lib/jgit/BUCK b/lib/jgit/BUCK index dcefb64..9dc2e73 100644 --- a/lib/jgit/BUCK +++ b/lib/jgit/BUCK
@@ -1,13 +1,13 @@ include_defs('//lib/maven.defs') -REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL. -VERS = '4.0.0.201505050340-m2' +REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL. +VERS = '4.0.0.201505191015-rc1.19-g1773002' maven_jar( name = 'jgit', id = 'org.eclipse.jgit:org.eclipse.jgit:' + VERS, - bin_sha1 = '1cc3120d39ed2b55584e631634e65c5d2e6c1cf7', - src_sha1 = '425f578cc9d5ccb8f3b050a5ab1e2d7a0becb25d', + bin_sha1 = '4db24b39dab8dc0e889807383728032945f461be', + src_sha1 = '1723a2855f50493b7c0b216aae97909a7ea59962', license = 'jgit', repository = REPO, unsign = True, @@ -22,7 +22,7 @@ maven_jar( name = 'jgit-servlet', id = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + VERS, - sha1 = '2a9f55d1d92afef795542b995db6ab261007857f', + sha1 = '7bfdbddea56a87f3f2687ae6abf2c5bdae649f0c', license = 'jgit', repository = REPO, deps = [':jgit'], @@ -36,7 +36,7 @@ maven_jar( name = 'jgit-archive', id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + VERS, - sha1 = 'ee3954753067818f8f734981a01c13ac33425f2c', + sha1 = '08fce6b89f6d1e78f99869d542d70899f3be9c9f', license = 'jgit', repository = REPO, deps = [':jgit', @@ -53,7 +53,7 @@ maven_jar( name = 'junit', id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + VERS, - sha1 = '6cc19f8f0a1791e26d4225625ecba6a31d9b830e', + sha1 = 'a54c16076e6cbdb9113565a82cffa5f268ae8e3b', license = 'DO_NOT_DISTRIBUTE', repository = REPO, unsign = True,
diff --git a/lib/joda/BUCK b/lib/joda/BUCK index d45ce78..2401870 100644 --- a/lib/joda/BUCK +++ b/lib/joda/BUCK
@@ -7,8 +7,8 @@ maven_jar( name = 'joda-time', - id = 'joda-time:joda-time:2.3', - sha1 = '56498efd17752898cfcc3868c1b6211a07b12b8f', + id = 'joda-time:joda-time:2.7', + sha1 = '5599707a3eaad13e889f691b3af78c8c03842195', deps = [':joda-convert'], license = 'Apache2.0', exclude = EXCLUDE,
diff --git a/lib/log/BUCK b/lib/log/BUCK index cadc7e7..b332f20 100644 --- a/lib/log/BUCK +++ b/lib/log/BUCK
@@ -31,3 +31,18 @@ license = 'Apache2.0', exclude = ['META-INF/LICENSE', 'META-INF/NOTICE'], ) + +maven_jar( + name = 'jsonevent-layout', + id = 'net.logstash.log4j:jsonevent-layout:1.7', + sha1 = '507713504f0ddb75ba512f62763519c43cf46fde', + license = 'Apache2.0', + deps = [':json-smart', '//lib/commons:lang'] +) + +maven_jar( + name = 'json-smart', + id = 'net.minidev:json-smart:1.1.1', + sha1 = '24a2f903d25e004de30ac602c5b47f2d4e420a59', + license = 'Apache2.0', +)
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK index 9026f79..68d579d 100644 --- a/lib/lucene/BUCK +++ b/lib/lucene/BUCK
@@ -1,11 +1,11 @@ include_defs('//lib/maven.defs') -VERSION = '4.10.2' +VERSION = '5.1.0' maven_jar( name = 'core', id = 'org.apache.lucene:lucene-core:' + VERSION, - sha1 = 'c01e3d675d277e0a93e7890d03cc3246b2cdecaa', + sha1 = '93e64c67106f9a50e6ea01cfcfd6ac692ab3a41a', license = 'Apache2.0', exclude = [ 'META-INF/LICENSE.txt', @@ -16,8 +16,33 @@ maven_jar( name = 'analyzers-common', id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION, - sha1 = 'f977f8c443e8f4e9d1fd7fdfda80a6cf60b3e7c2', + sha1 = '54770d9b792536dff25ae1d70cd8af822c0079a3', license = 'Apache2.0', + deps = [':core'], + exclude = [ + 'META-INF/LICENSE.txt', + 'META-INF/NOTICE.txt', + ], +) + +maven_jar( + name = 'backward-codecs', + id = 'org.apache.lucene:lucene-backward-codecs:' + VERSION, + sha1 = '5f0c5bb10ac3facace6b314bb02a6b572795b3c9', + license = 'Apache2.0', + deps = [':core'], + exclude = [ + 'META-INF/LICENSE.txt', + 'META-INF/NOTICE.txt', + ], +) + +maven_jar( + name = 'misc', + id = 'org.apache.lucene:lucene-misc:' + VERSION, + sha1 = '3b700fa57f5d444da0e58cc1855042e6c5a18640', + license = 'Apache2.0', + deps = [':core'], exclude = [ 'META-INF/LICENSE.txt', 'META-INF/NOTICE.txt', @@ -27,6 +52,11 @@ maven_jar( name = 'query-parser', id = 'org.apache.lucene:lucene-queryparser:' + VERSION, - sha1 = 'd70f54e1060d553ba7aeb4d49a71fd0c068499e8', + sha1 = '53f0b3f0e700a8ec484195d3370688171e830634', license = 'Apache2.0', + deps = [':core'], + exclude = [ + 'META-INF/LICENSE.txt', + 'META-INF/NOTICE.txt', + ], )
diff --git a/lib/maven.defs b/lib/maven.defs index 4edba9c..cc45212 100644 --- a/lib/maven.defs +++ b/lib/maven.defs
@@ -46,9 +46,14 @@ from os import path parts = id.split(':') - if len(parts) != 3: - raise NameError('expected id="groupId:artifactId:version"') - group, artifact, version = parts + if len(parts) not in [3, 4]: + raise NameError('%s:\nexpected id="groupId:artifactId:version[:classifier]"' + % id) + if len(parts) == 4: + group, artifact, version, classifier = parts + else: + group, artifact, version = parts + classifier = None # SNAPSHOT artifacts are handled differently on Google storage bucket: # 'SNAPSHOT' is discarded from the directory name. However on other @@ -62,7 +67,11 @@ else: file_version = version + if classifier is not None: + file_version += '-' + classifier + jar = path.join(name, artifact.lower() + '-' + file_version) + url = '/'.join([ repository, group.replace('.', '/'), artifact, version,
diff --git a/lib/prolog/BUCK b/lib/prolog/BUCK index 1f3e425..77fe5ac 100644 --- a/lib/prolog/BUCK +++ b/lib/prolog/BUCK
@@ -1,15 +1,53 @@ include_defs('//lib/maven.defs') +VERSION = '1.4.1' +REPO = GERRIT + maven_jar( - name = 'prolog-cafe', - id = 'com.googlecode.prolog-cafe:PrologCafe:1.3', - sha1 = '5e0fbf18e8c98c4113f9acc978306884a1152870', + name = 'runtime', + id = 'com.googlecode.prolog-cafe:prolog-runtime:' + VERSION, + sha1 = 'c5d9f92e49c485969dcd424dfc0c08125b5f8246', license = 'prologcafe', - repository = GERRIT, + repository = REPO, +) + +maven_jar( + name = 'compiler', + id = 'com.googlecode.prolog-cafe:prolog-compiler:' + VERSION, + sha1 = 'ac24044c6ec166fdcb352b78b80d187ead3eff41', + license = 'prologcafe', + repository = REPO, + deps = [ + ':io', + ':runtime', + ], +) + +maven_jar( + name = 'io', + id = 'com.googlecode.prolog-cafe:prolog-io:' + VERSION, + sha1 = 'b072426a4b1b8af5e914026d298ee0358a8bb5aa', + license = 'prologcafe', + repository = REPO, + deps = [':runtime'], + visibility = [], +) + +maven_jar( + name = 'cafeteria', + id = 'com.googlecode.prolog-cafe:prolog-cafeteria:' + VERSION, + sha1 = '8cbc3b0c19e7167c42d3f11667b21cb21ddec641', + license = 'prologcafe', + repository = REPO, + deps = [ + ':io', + ':runtime', + ], + visibility = ['//gerrit-pgm:'], ) java_binary( - name = 'compiler', + name = 'compiler_bin', main_class = 'BuckPrologCompiler', deps = [':compiler_lib'], visibility = ['PUBLIC'], @@ -18,6 +56,9 @@ java_library( name = 'compiler_lib', srcs = ['java/BuckPrologCompiler.java'], - deps = [':prolog-cafe'], + deps = [ + ':compiler', + ':runtime', + ], visibility = ['//tools/eclipse:classpath'], )
diff --git a/lib/prolog/java/BuckPrologCompiler.java b/lib/prolog/java/BuckPrologCompiler.java index 17d2d76..0cbe10e 100644 --- a/lib/prolog/java/BuckPrologCompiler.java +++ b/lib/prolog/java/BuckPrologCompiler.java
@@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import com.googlecode.prolog_cafe.compiler.CompileException; import com.googlecode.prolog_cafe.compiler.Compiler; +import com.googlecode.prolog_cafe.exceptions.CompileException; import java.io.File; import java.io.FileInputStream; @@ -63,7 +63,11 @@ private static void add(JarOutputStream out, File classes, String prefix) throws IOException { - for (String name : classes.list()) { + String[] list = classes.list(); + if (list == null) { + return; + } + for (String name : list) { File f = new File(classes, name); if (f.isDirectory()) { add(out, f, prefix + name + "/"); @@ -71,8 +75,7 @@ } JarEntry e = new JarEntry(prefix + name); - FileInputStream in = new FileInputStream(f); - try { + try (FileInputStream in = new FileInputStream(f)) { e.setTime(f.lastModified()); out.putNextEntry(e); byte[] buf = new byte[16 << 10]; @@ -81,7 +84,6 @@ out.write(buf, 0, n); } } finally { - in.close(); out.closeEntry(); } }
diff --git a/lib/prolog/prolog.defs b/lib/prolog/prolog.defs index d0636f5..677a9e2 100644 --- a/lib/prolog/prolog.defs +++ b/lib/prolog/prolog.defs
@@ -19,7 +19,7 @@ visibility = []): genrule( name = name + '__pl2j', - cmd = '$(exe //lib/prolog:compiler)' + + cmd = '$(exe //lib/prolog:compiler_bin)' + ' $TMP $OUT ' + ' '.join(srcs), srcs = srcs, @@ -28,7 +28,7 @@ java_library( name = name + '__lib', srcs = [':' + name + '__pl2j'], - deps = ['//lib/prolog:prolog-cafe'] + deps, + deps = ['//lib/prolog:runtime'] + deps, ) genrule( name = name + '__ln',
diff --git a/plugins/commit-message-length-validator b/plugins/commit-message-length-validator index 7923b67..8d295ed 160000 --- a/plugins/commit-message-length-validator +++ b/plugins/commit-message-length-validator
@@ -1 +1 @@ -Subproject commit 7923b67392164dcc65ada85f723fa5111b265484 +Subproject commit 8d295ed48e8f52eef5661b6eb10d6402d197c776
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin index 17b63c1..a93641d 160000 --- a/plugins/cookbook-plugin +++ b/plugins/cookbook-plugin
@@ -1 +1 @@ -Subproject commit 17b63c160498d02fb1c511c5b43b02f538b29558 +Subproject commit a93641db50f52e24421f75671bb1d4df268dd722
diff --git a/plugins/download-commands b/plugins/download-commands index baa09c2..1cf6921 160000 --- a/plugins/download-commands +++ b/plugins/download-commands
@@ -1 +1 @@ -Subproject commit baa09c2e265a2b264a5fb4571e7eefda04def0c4 +Subproject commit 1cf69212a7489e88d8c73377f0f77f8a5965db75
diff --git a/plugins/replication b/plugins/replication index 53ee1b8..f03316c 160000 --- a/plugins/replication +++ b/plugins/replication
@@ -1 +1 @@ -Subproject commit 53ee1b8ec4de5de4d710233eda2230b5380f1390 +Subproject commit f03316c26b5991cba96280de59a4119619a18a58
diff --git a/plugins/reviewnotes b/plugins/reviewnotes index 603b7b3..b5c6f81 160000 --- a/plugins/reviewnotes +++ b/plugins/reviewnotes
@@ -1 +1 @@ -Subproject commit 603b7b3885a1d2953ca891f58d2a6095e72e5313 +Subproject commit b5c6f81d979e78fbd734d946b18270ec8319eaf6
diff --git a/tools/build.defs b/tools/build.defs index 8b858cd..da07c1e 100644 --- a/tools/build.defs +++ b/tools/build.defs
@@ -71,8 +71,8 @@ context = [ '//gerrit-main:main_bin', '//gerrit-war:webapp_assets', - '//gerrit-gwtui:' + ui, - ] + context, + ] + (['//gerrit-gwtui:' + ui] if ui else []) + + context, docs = docs, visibility = visibility, )
diff --git a/tools/checkstyle.xml b/tools/checkstyle.xml index a9f8cfe..fc94144 100644 --- a/tools/checkstyle.xml +++ b/tools/checkstyle.xml
@@ -14,10 +14,12 @@ <property name="severity" value="warning"/> <property name="charset" value="UTF-8"/> <module name="TreeWalker"> + <module name="FileContentsHolder"/> <module name="OuterTypeFilename"/> <module name="LineLength"> <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> - <property name="max" value="100"/> + <property name="max" value="150"/> + <property name="tabWidth" value="2"/> </module> <module name="OneTopLevelClass"/> <module name="NoLineWrap"/> @@ -27,9 +29,8 @@ </module> <module name="NeedBraces"/> <module name="LeftCurly"> - <property name="maxLineLength" value="100"/> + <property name="maxLineLength" value="150"/> </module> - <module name="RightCurly"/> <module name="RightCurly"> <property name="option" value="alone"/> <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/> @@ -47,8 +48,6 @@ <module name="OneStatementPerLine"/> <module name="MultipleVariableDeclarations"/> <module name="ArrayTypeStyle"/> - <module name="MissingSwitchDefault"/> - <module name="FallThrough"/> <module name="UpperEll"/> <module name="ModifierOrder"/> <module name="EmptyLineSeparator"> @@ -100,4 +99,9 @@ <property name="eachLine" value="true"/> <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/> </module> + <module name="SuppressWithNearbyCommentFilter"> + <property name="commentFormat" value="CS IGNORE (\w+) FOR NEXT (\d+) LINES\. REASON\: \w+"/> + <property name="checkFormat" value="$1"/> + <property name="influenceFormat" value="$2"/> + </module> </module>
diff --git a/tools/default.defs b/tools/default.defs index 30518c4..14bc9ce 100644 --- a/tools/default.defs +++ b/tools/default.defs
@@ -18,6 +18,7 @@ include_defs('//tools/gwt-constants.defs') include_defs('//tools/java_doc.defs') include_defs('//tools/java_sources.defs') +include_defs('//tools/git.defs') import copy # Set defaults on java rules: @@ -63,7 +64,8 @@ kwargs[apdk] = [] apds = kwargs.get(apdk, []) - if AUTO_VALUE_DEP in kwargs.get('deps', []): + all_deps = kwargs.get('deps', []) + kwargs.get('exported_deps', []) + if AUTO_VALUE_DEP in all_deps: aps.extend(AUTO_VALUE_PROCESSORS) apds.extend(AUTO_VALUE_PROCESSOR_DEPS) @@ -129,7 +131,7 @@ type = 'plugin', visibility = ['PUBLIC']): from multiprocessing import cpu_count - mf_cmd = 'v=\$(git describe HEAD);' + mf_cmd = 'v=%s;' % git_describe(name) if manifest_file: mf_src = [manifest_file] mf_cmd += 'sed "s:@VERSION@:$v:g" $SRCS >$OUT'
diff --git a/tools/gerrit.importorder b/tools/gerrit.importorder index 831c5fe..398130e 100644 --- a/tools/gerrit.importorder +++ b/tools/gerrit.importorder
@@ -1,9 +1,12 @@ #Organize Import Order -#Wed Jan 14 10:19:45 JST 2015 -6=javax -5=java -4=org -3=net -2=junit -1=com -0=com.google +#Mon Mar 23 17:27:34 PDT 2015 +9=javax +8=java +7=org +6=net +5=junit +4=eu +3=dk +2=com +1=com.google +0=\#
diff --git a/tools/git.defs b/tools/git.defs index c58ac88..0247e32 100644 --- a/tools/git.defs +++ b/tools/git.defs
@@ -12,10 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -def git_describe(): +def git_describe(plugin = None): import subprocess - cmd = ['git', 'describe', '--match', 'v[0-9].*', '--dirty'] - p = subprocess.Popen(cmd, stdout = subprocess.PIPE) + cmd = ['git', 'describe', '--always', '--match', 'v[0-9].*', '--dirty'] + if not plugin or plugin == '${pluginName}': + p = subprocess.Popen(cmd, stdout = subprocess.PIPE) + else: + p = subprocess.Popen(cmd, stdout = subprocess.PIPE, cwd = 'plugins/%s' % plugin) v = p.communicate()[0].strip() r = p.returncode if r != 0: